#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ncurses.h>
#include <algorithm>
typedef uint16_t* pi16;

const char* room_str[] = {
"    #####          ",
"    #---#          ",
"    #@--#          ",
"  ###--@##         ",
"  #--@-@-#         ",
"###-#-##-#   ######",
"#---#-##-#####--**#",
"#-@--@----------**#",
"#####-###-#+##--**#",
"    #-----#########",
"    #######        "};
char room[32][32];
int  W, H; // box被ncurses占用了,换拼音
char man[2], xz[8][2], goal[8][2]; // (x,y)坐标
int  n_xz; // 箱子数=目标数
char old_man[2], old_xz[2]; // 旧坐标
int  can_undo, old_xz_idx = -1;

void parse_room () {
  W = strlen(room_str[0]); H = sizeof(room_str) / sizeof(room_str[0]);
  int n = 0;
  for (int y = 0; y < H; y++) for (int x = 0; x < W; x++) {
    char  c = '-';
    switch (room_str[y][x]) {
    case '+': man[0] = x; man[1] = y; break;
    case '@': xz[n_xz][0] = x; xz[n_xz++][1] = y; break;
    case '*': goal[n][0] = x; goal[n++][1] = y; break;
    default: c = room_str[y][x];
    }
    room[y][x] = c;
  }
  std::sort(pi16(xz), pi16(xz + n)); std::sort(pi16(goal), pi16(goal + n));
}

void draw_ch (int x, int y, int ch, int color) {
  int attr = COLOR_PAIR(color);
  if (ch != ' ' && ch != '-') attron(attr);
  mvaddch(3 + y, 30 + x, ch);
  if (ch != ' ' && ch != '-') attroff(attr);
}

void draw () {
  for (int x = 0; x < 16; x++) mvprintw(2, 30+x, "%x", x);
  for (int y = 0; y < 11; y++) mvprintw(3+y, 29, "%x", y);
  for (int i = 0; i < n_xz; i++) mvprintw(1, 20+i*6, "%d,%d", xz[i][0], xz[i][1]);
  for (int y = 0; y < H; y++) for (int x = 0; x < W; x++) draw_ch(x, y, room[y][x], 1);
  for (int i = 0; i < n_xz; i++) draw_ch(goal[i][0], goal[i][1], '*', 2), draw_ch(xz[i][0], xz[i][1], '@', 3);
  draw_ch(man[0], man[1], '+', 4);
  refresh();
}

void init_ncurses () {
  initscr(); start_color();
  init_pair(1, COLOR_BLUE, COLOR_BLUE);
  init_pair(2, COLOR_YELLOW, COLOR_BLACK);
  init_pair(3, COLOR_GREEN, COLOR_BLACK);
  init_pair(4, COLOR_RED, COLOR_BLACK);
  cbreak(); noecho(); keypad(stdscr, 1); curs_set(0);
  //timeout(0); let getch() block; ch == ERR
  atexit((void (*)())endwin);
}

#define XY(x,y) (((y) << 8) | (x))

inline int get_xz (uint16_t xy) {
  int low = 0, high = n_xz - 1;
  while (low <= high) {
    const int m = (low + high) >> 1;
    const uint16_t xy0 = *(uint16_t*)xz[m];
    if (xy0 == xy) return m;
    else if (xy0 < xy) low = m + 1;
    else high = m - 1;
  }
  return -1;
}

bool move (int d) {
  static const char D[][2] = { 0, -1, 0, 1, -1, 0, 1, 0 };
  int x = man[0], y = man[1];
  if (room[y += D[d][1]][x += D[d][0]] == '#') return false;
  int i = get_xz(XY(x,y));
  if (i != -1) {
    if (room[y += D[d][1]][x += D[d][0]] == '#') return false;
    int j = get_xz(XY(x,y));
    if (j != -1) return false;
    *((uint16_t*)old_xz) = *((uint16_t*)xz[i]);
    int x = xz[i][0] += D[d][0], y = xz[i][1] += D[d][1];
    std::sort(pi16(xz), pi16(xz + n_xz));
    bool f0 = room[y-1][x] == '#' || get_xz(XY(x, y-1)) != -1;
    bool f1 = room[y+1][x] == '#' || get_xz(XY(x, y+1)) != -1;
    bool f2 = room[y][x-1] == '#' || get_xz(XY(x-1, y)) != -1;
    bool f3 = room[y][x+1] == '#' || get_xz(XY(x+1, y)) != -1;
    mvprintw(16,20, "(%x,%x):%d (%x,%x):%d (%x,%x):%d (%x,%x):%d", x,y-1,f0, x,y+1,f1, x-1,y,f2, x+1,y,f3);
  }
  *((uint16_t*)old_man) = *((uint16_t*)man); man[0] += D[d][0]; man[1] += D[d][1];
  old_xz_idx = i; can_undo = 1; return true;
}

void undo () {
  if (!can_undo) return;
  *((uint16_t*)man) = *((uint16_t*)old_man);
  if (old_xz_idx != -1) *((uint16_t*)xz[old_xz_idx]) = *((uint16_t*)old_xz);
  old_xz_idx = -1; can_undo = 0;
}

int main () {
  for (parse_room(), init_ncurses();;) {
    draw();
    int ch = getch();
    if (ch == KEY_UP || ch == 'w') move(0);
    else if (ch == KEY_DOWN || ch == 'x') move(1);
    else if (ch == KEY_LEFT || ch == 'a') move(2);
    else if (ch == KEY_RIGHT || ch == 'd') move(3);
    else if (ch == 'u') undo();
    else if (ch == 'q') break;
  }
  return 0;
} // usleep(10000);
View Code

截图是较早处理' '和'-'有bug的版本。

箱子会动,没法简单判断是否是死局:暂时推不动,推开别的箱子后能推动了。

试了下算箱子和目标的坐标向量的点积。程序应该没错,完全重合时是1,快推出来时接近1,别的时候堪做反向指标。

2025-11-02 00-58-50屏幕截图

人能一目了然,视线还可以拐弯。也许我得学神经网络了。

也许可以:

  • 用畅通度打分。扫描每个箱子的水平和竖直方向,看有无目标、离目标的距离,途中障碍物的多少。
  • 在墙外再包一层,move前放所有箱子,move后拿走,不用坐标,只用字符来判断碰撞。
  • move后可能产生死局。箱子能在某方向移动的条件(以竖直方向为例)是上下都是空。如果两个箱子在比如水平方向挨着,竖直方向都不能动,则陷入死局。

用了大字体xterm:

XTerm*faceName: DejaVu Sans Mono : antialias=True : pixelsize=32
XTerm*faceNameDoubleSize :Noto Sans CJK : antialias=True : pixelsize=32

 

posted on 2025-11-01 14:50  华容道专家  阅读(0)  评论(0)    收藏  举报