4阶拼图求解

/* 
 *Developed by E^iπ
 *Copy and edit if you like
 *Free for any use
 */
#define WINDOWS 1

#ifdef WINDOWS
#include <windows.h>
#endif
 
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
#include <conio.h>

/* I decided to use C, not C++, at first, but I find that I don't want to make 
   a set myself... */
#include <set>

using namespace std;

#define U64 unsigned long long

/* To get bits */
const U64 base[16] = { 0xf, 0xf0, 0xf00,
  0xf000, 0xf0000, 0xf00000,
  0xf000000, 0xf0000000,
  0xf00000000llu, 0xf000000000llu,
  0xf0000000000llu, 0xf00000000000llu,
  0xf000000000000llu,
  0xf0000000000000llu,
  0xf00000000000000llu,
  0xf000000000000000llu
};

U64 *makerbase() {
  U64 *rb = new U64[16];
  for (int i = 0; i < 16; ++i) {
    rb[i] = ~base[i];
  }
  return rb;
}
const U64 *rbase = makerbase();

/* Use a U64 to record the field. each 4bits refers to a number. *Notice that:
   *Storage:(in 4 radix) 
   *00 01 02 03 
   *10 11 12 13 
   *20 21 22 23 
   *30 31 32 33
   *Players see:  
   *01 02 03 04 
   *05 06 07 08 
   *09 10 11 12 
   *13 14 15 00(in Windows, 0 is blank) */
const U64 ac = 0xfedcba9876543210llu;
U64 now;

const char U_ = 0, D_ = 1, L_ = 2, IMPORT = 19,
  R_ = 3, RANDF = 17, AC = 33, FAIL = -2, NEXTI = 18, ILLACT = -1;
  // reverse direction
#define REV(DR) ((DR)^1)

// P means the place of '0'(seen) or 0xf(stored) in field, which can be moved.
char nowp;

#define U_ABLE(P) ((P)>3)
#define U_P(P) ((P)-4)
#define D_ABLE(P) ((P)<12)
#define D_P(P) ((P)+4)
#define L_ABLE(P) ((P)&3)
#define L_P(P) ((P)-1)
#define R_ABLE(P) (((P)&3)^3)
#define R_P(P) ((P)+1)

#define up(F,P) (F&rbase[P]&rbase[P - 4] | ((F&base[P]) >> 16) |((F&base[P- 4]) << 16))

#define down(F,P) (F&rbase[P]&rbase[P+ 4] | ((F&base[P])<<16) |((F&base[P+ 4]) >> 16))

#define left(F,P) (F&rbase[P]&rbase[P - 1] | ((F&base[P]) >>4) |((F&base[P- 1]) << 4))

#define right(F,P) (F&rbase[P]&rbase[P+ 1] | ((F&base[P])<<4) |((F&base[P+ 1]) >> 4))

void output(U64 o) {
  for (int i = 0; i < 4; ++i) {
    for (int j = 0; j < 4; ++j) {
      printf("%02llu ", o + 1 & 15);
      o >>= 4;
    }
    printf("\n\n");
  }
}

inline char conv(char act) {
  if (act == '2' || act == 'w')
    return U_;
  if (act == '8' || act == 's')
    return D_;
  if (act == '4' || act == 'a')
    return L_;
  if (act == '6' || act == 'd')
    return R_;
  if (act == 'r' || act == '0')
    return RANDF;
  if (act == 'h' || act == '1')
    return NEXTI;
  if (act == 'i' || act == '3')
    return IMPORT;
  return ILLACT;
}

inline bool act(U64 & f, char &p, char act) {
  if (act == ILLACT) {
    return false;
  } else if (act == U_) {
    if (U_ABLE(p)) {
      f = up(f, p);
      p = U_P(p);
    } else {
      return false;
    }
  } else if (act == D_) {
    if (D_ABLE(p)) {
      f = down(f, p);
      p = D_P(p);
    } else {
      return false;
    }
  } else if (act == L_) {
    if (L_ABLE(p)) {
      f = left(f, p);
      p = L_P(p);
    } else {
      return false;
    }
  } else if (act == R_) {
    if (R_ABLE(p)) {
      f = right(f, p);
      p = R_P(p);
    } else {
      return false;
    }
  }
  return true;
}

int xtime() {
#ifdef LINUX
  static int t;
  static struct timeval tmv;
  gettimeofday(&tmv, NULL);
  t = 1000 * tmv.tv_sec + tmv.tv_usec/1000;
  return t;
#elifdef WINDOWS
  return GetTickCount();
#endif
}

void randmove(U64 & f, char &p, int step) {
  while (step--) {
    act(f, p, rand() & 3);
  }
}

/* Aid function for solve */
int getU(U64 F) {
  int r = 0;
  U64 comp = ac;
  for (int i = 32; i--;) {
    // the distant to ac.
    r += abs((int)(F & 3) - (comp & 3));
    F >>= 2;
    comp >>= 2;
  }
  return r;
}

/* Balance between least cost and shortest answer */
int factor;

// To use std::set, we need a class.
class site {
  friend bool operator <(const site & a, const site & b);
public:
    U64 f;
  unsigned short depth, U;
  char op, p;
    site() {
  } site(U64 gf, int gp, int dpth, char o) {
    f = gf, p = gp;
    op = o;                     // last operate.
    depth = dpth;
  }
  site(const site & g) {
    (*this) = g;
  }
  inline site & operator  =(const site & g) {
    f = g.f, p = g.p;
    depth = g.depth;
    op = g.op;
    U = g.U;
    return *this;
  }
  inline int gU() {
    return U = getU(f) + (depth>>factor);
  }
};

inline bool operator <(const site & a, const site & b) {
  return a.U < b.U ? true : (a.U == b.U && a.f < b.f);
}

set <site> doing, done;
/* 
   MAIN ALGORITHM: A* algorithm, a way between Brute-Force and BFS, search the 
   node with least U each time. */
int solve(U64 f, int p, char *r) {
  int t = time(0);
  doing.clear();
  done.clear();
  site n(f, p, 0, 5), k;
  k.depth = 0;
  n.gU();
  doing.insert(n);
  while (!doing.empty()) {
    if(time(0)-t>4) {
        return -10;
    }
    n = *doing.begin();
    doing.erase(doing.begin());
    done.insert(n);
    if(n.depth>100) continue;
    k.depth = n.depth + 1;
    if (n.f == ac)
      break;
    /* n.op!=1 stop us from go back, not necessary, but save time. */
    if (n.op != 1 && U_ABLE(n.p)) {
      k.f = up(n.f, n.p);
      k.p = U_P(n.p), k.op = 0;
      k.gU();
      if (done.find(k) == done.end()) {
        doing.insert(k);
      }
    }
    if (n.op != 0 && D_ABLE(n.p)) {
      k.f = down(n.f, n.p);
      k.p = D_P(n.p);
      k.gU();
      k.op = 1;
      if (done.find(k) == done.end()) {
        doing.insert(k);
      }
    }
    if (n.op != 3 && L_ABLE(n.p)) {
      k.f = left(n.f, n.p);
      k.p = L_P(n.p);
      k.gU();
      k.op = 2;
      if (done.find(k) == done.end()) {
        doing.insert(k);
      }
    }
    if (n.op != 2 && R_ABLE(n.p)) {
      k.f = right(n.f, n.p);
      k.p = R_P(n.p);
      k.gU();
      k.op = 3;
      if (done.find(k) == done.end()) {
        doing.insert(k);
      }
    }
  }
  /* The answer is REVERSED!!! This function will return the index of last
     move(stored in r). */
  int i = 0;
  while (n.op != 5) {
    // arrived beginning
    r[i++] = (n.op);
    n.depth--;
    // go back
    act(n.f, n.p, REV(n.op));
    n.gU();
    n = *done.find(n);
  }
  printf("\n%d,%d\n",doing.size(),done.size());
  fflush(stdout);
  getche();
  return i - 1;
}
#ifdef WINDOWS
    #define CLEAR system("cls")
#else
    #define CLEAR clrscr();
#endif


int main() {
  srand(time(0)^19260817);
  now = ac;
  nowp = 15;
  bool aid=false;
  int step = 0, i, in;
  char *s = new char[1024],move;
  const char op[4][4]{"","","",""};
  while (1) {
      CLEAR;
    output(now);
    printf("%d", step);
    if(aid){
        aid=false;
        printf("step required:%d\n", i);
        while(i>=0) {
            printf("%s",op[s[i--]]);
            if(i%5==0) printf("\n");
        }
        fflush(stdout);
    }
    fflush(stdout);
    move = conv(getche());
    if (move == RANDF) {
      step = 0;
      randmove(now, nowp, 666233);
    } else if (move == NEXTI && now != ac) {
      // If cant get short answer, add factor to get a longer one.
      for (factor = 0; (i = solve(now, nowp, s))
           < -1; factor++);
      act(now, nowp, s[i]);
      step++;
      aid=true;
    } else if(move==IMPORT){
        now=0;
        for(i=0;i<16;++i){
            scanf("%d",&in);
            CLEAR;
            if(in==0) nowp=i;
            now|=(((U64)(in-1)&15)<<i*4);
            output(now);
            fflush(stdout);
        }
    } else {
      if (act(now, nowp, move))
        step++;
    }
  }
}

 

posted @ 2017-04-29 21:06  141421356  阅读(552)  评论(0)    收藏  举报