Форум программистов, компьютерный форум, киберфорум
Hadros
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  

Вариант 1, рекурсивный перебор ходов

Запись от Hadros размещена 17.11.2015 в 23:09
Показов 1229 Комментарии 1
Метки delphi

Эта запись является продолжением записи Варианты

И так, всё поле хранится в массиве 16x16.
Для того, чтобы можно было как-то зафиксировать полученное решение, нужно организовать запись всех ходов. Каждый ход может начинаться в одной какой-то клетке и происходить в одном из четырёх направлений. Для экономии, можно занумеровать все клетки числами от 0 до 255 - это займёт 1 байт. На запись направления достаточно 2 бита, но смысла заморачиваться с битами нет, так-что пусть на направление тоже будет выделен 1 байт, но значения в этом байте будут только от 0 до 3. Вверх - 0. Вниз - 1. Влево - 2. Вправо - 3.
Для определения, на какой глубине мы сейчас находимся нужна ещё одна переменная. Т.к. максимальная глубина не может превышать количество фишек на поле, то для этой переменной достаточно одного байта. И, для того, чтобы понять, что мы выиграли, нужна переменная, в которой будет храниться выигрышная глубина. Её надо рассчитать перед вызовом функции, посчитав количество фишек на поле и вычтя единицу.

Ниже, вариант рекурсивной функции, которая ищет решение. К сожалению, у меня не сохранилось оригинала, так-что пришлось написать её заново специально для блога.
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
unit PoleWork_Function;
 
interface
 
Type
  TSteps=record
    p:byte;                           // стартовая позиция
    n:byte;                           // направление
  end;
Var
  Pole:array[0..15,0..15] of byte;    // поле
  Steps:array[0..222] of TSteps;      // запись пути
  StepsCount:byte;                    // текущая глубина
  WinSteps:byte;                      // глубина, при достижении которой происходит выигрыш
                                      // WinSteps := (<исходное количество фишек> - 1)
 
function PoleWork:boolean;
 
implementation
 
function PoleWork:boolean;
var
  x,y:byte;     // координаты позиции, с которой делается ход
begin
  for x:=1 to 14 do
    for y:=1 to 14 do
      if Pole[x,y]=1 then
        begin
          if (Pole[x,y-1]=1) and (Pole[x,y-2]=0) then
            begin                         // Если оба условия выполняются, значит возможен ход ВВЕРХ. Сделать этот ход:
              Pole[x,y]:=0;               // взять фишку с текущей позиции
              Pole[x,y-1]:=0;             // убрать перепрыгиваемую фишку
              Pole[x,y-2]:=1;             // поставить фишку в новое место
              Steps[StepsCount].p:=x*16+y;// cохранить позицию, с которой сделан последний ход
              Steps[StepsCount].n:=0;     // и направление хода (ВВЕРХ)
              inc(StepsCount);            // увеличить глубину
              if PoleWork then
                begin                     // если более глубокий вызов PoleWork вернёт TRUE, значит решение найдено
                  Result:=true;           // нужно вернуть TRUE внешней функции
                  exit;                   // и просто закончить
                end else
                begin                     // если более глубокий вызов PoleWork вернёт FALSE, значит это тупиковая ветка
                                          // и нужно вернуться на ход назад
                  Pole[x,y-2]:=0;         // взять фишку с нового места
                  Pole[x,y-1]:=1;         // вернуть перепрыгнутую фишку
                  Pole[x,y]:=1;           // вернуть взятую фишку в текущую позицию
                  dec(StepsCount);        // вернутся в цепочке на прежнюю глубину
                end;
            end;
          if (Pole[x,y+1]=1) and (Pole[x,y+2]=0) then
            begin                         // Если оба условия выполняются, значит возможен ход ВНИЗ. Сделать этот ход:
              Pole[x,y]:=0;
              Pole[x,y+1]:=0;
              Pole[x,y+2]:=1;
              Steps[StepsCount].p:=x*16+y;
              Steps[StepsCount].n:=1;     // направление хода: ВНИЗ
              inc(StepsCount);
              if PoleWork then
                begin
                  Result:=true;
                  exit;
                end else
                begin
                  Pole[x,y+2]:=0;
                  Pole[x,y+1]:=1;
                  Pole[x,y]:=1;
                  dec(StepsCount);
                end;
            end;
          if (Pole[x-1,y]=1) and (Pole[x-2,y]=0) then
            begin                         // Если оба условия выполняются, значит возможен ход ВЛЕВО. Сделать этот ход:
              Pole[x,y]:=0;
              Pole[x-1,y]:=0;
              Pole[x-2,y]:=1;
              Steps[StepsCount].p:=x*16+y;
              Steps[StepsCount].n:=2;     // направление хода: ВЛЕВО
              inc(StepsCount);
              if PoleWork then
                begin
                  Result:=true;
                  exit;
                end else
                begin
                  Pole[x-2,y]:=0;
                  Pole[x-1,y]:=1;
                  Pole[x,y]:=1;
                  dec(StepsCount);
                end;
            end;
          if (Pole[x+1,y]=1) and (Pole[x+2,y]=0) then
            begin                         // Если оба условия выполняются, значит возможен ход ВПРАВО. Сделать этот ход:
              Pole[x,y]:=0;
              Pole[x+1,y]:=0;
              Pole[x+2,y]:=1;
              Steps[StepsCount].p:=x*16+y;
              Steps[StepsCount].n:=3;     // направление хода: ВПРАВО
              inc(StepsCount);
              if PoleWork then
                begin
                  Result:=true;
                  exit;
                end else
                begin
                  Pole[x+2,y]:=0;
                  Pole[x+1,y]:=1;
                  Pole[x,y]:=1;
                  dec(StepsCount);
                end;
            end;
        end;
  Result:=(StepsCount>=WinSteps);         // Если StepsCount=WinSteps, то на поле осталась 1 фишка и мы победили
                                          // решение будет записано в массив Steps
end;
end.
Перед вызовом функции нужно заполнить массив Pole. Значение 0 - пустая клетка, значение 1 - фишка, любое другое значение - граница поля. Также, нужно задать начальную глубину StepsCount:=0.

Всё прекрасно работает, решения находятся. Но есть большая проблема со скоростью. Даже с 17-ю начальными фишками решение ищется несколько минут. А у нас в исходной задаче 80 фишек. А в обобщённом варианте вообще может быть 224.

Под спойлером простенькая консольная программа для поиска решений, использующая функцию PoleWork.
Кликните здесь для просмотра всего текста

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
program PoleWork_Delphi_program;
 
{$APPTYPE CONSOLE}
 
{$R *.res}
 
uses
  System.SysUtils, PoleWork_Function;
 
Const
  StateFileName='state_m1616.bgs000';
  LogFileName='PW.log';
 
function Load0State(fn:string):boolean;
var
  inputFile:file of byte;
  x,y:byte;
begin
  Result:=false;
  assign(inputFile,fn);
  try
    reset(inputFile);
    for x:=0 to 15 do for y:=0 to 15 do read(inputFile,Pole[x,y]);
    closefile(inputFile);
    Result:=true;
  except
    on E: Exception do Writeln(E.ClassName, ': ', E.Message);
  end;
end;
 
function SaveFinishLog(t:int64;r:boolean):boolean;
var
  logFile:textfile;
  h:byte;
begin
  Result:=false;
  assign(logFile,LogFileName);
  try
    if not fileexists(LogFileName) then rewrite(logFile) else append(logFile);
    writeln(logfile,DateTimeToStr(Now));
    writeln(logfile,inttostr(t));
    if r then
      begin
        writeln(logfile,'Решение:');
        for h:=0 to WinSteps-1 do writeln(logfile,inttostr(Steps[h].p div 16)+#32+inttostr(Steps[h].p mod 16)+#32+inttostr(Steps[h].n));
      end else writeln(logfile,'решений нет');
    writeln(logfile);
    closefile(logFile);
    Result:=true;
  except
    on E: Exception do Writeln(E.ClassName, ': ', E.Message);
  end;
end;
 
Var
  res:boolean;
  t1,t2:TDateTime;
  ms:int64;
  x,y,fsh:byte;
Begin
  writeln('Enter = Загрузить файл '+StateFileName);
  readln;
  if Load0State(StateFileName) then
    begin
      fsh:=0;
      for x:=0 to 16 do for y:=0 to 15 do if Pole[x,y]=1 then inc(fsh);
      WinSteps:=fsh-1;
      StepsCount:=0;
      writeln('Файл загружен. Количество фишек: '+inttostr(fsh)+'. Enter - начать поиск');
      readln;
      write('Поиск запущен '+DateTimeToStr(Now)+' ...');
      t1:=Now;
      res:=PoleWorkAsm(StepsCount,WinSteps,addr(Pole[0,0]),addr(Steps[0]));
      t2:=Now;
      ms:=round((t2-t1)*86400*1000);
      writeln;
      writeln('Времени потрачено (мс): '+inttostr(ms));
      if res then writeln('РЕШЕНИЕ НАЙДЕНО!') else writeln('нет решений');
      if not SaveFinishLog(ms,res) then writeln('Ошибка при сохранении в лог');
      writeln('Enter = Выход из программы');
      readln;
    end else
    begin
      writeln('Ошибка при загрузке файла '+StateFileName);
      readln;
    end;
end.
Начальное состояние берётся из файла с именем state_m1616.bgs000. Размер файла должен быть не меньше 256 байт. Пример файла Вложение 3472:
Решение, если оно есть, записывается прямо в лог PW.log.

Вложение 3473



Ассемблерный вариант PoleWork - PoleWorkAsm:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
function PoleWorkAsm(aSC,aWS:uint64;aPole,aSteps:pointer):boolean;
asm
        // RCX = aSC
        // RDX = aWC
        // R8 = aPole
        // R9 = aSteps
        // R10 = x
        // R11 = y
        // RBX, R12 - временные
        PUSH    RBX
        PUSH    R12
        CALL    @PW
        JMP     @finish
  @PW:
        MOV     R10,1
  @loop_x:
        MOV     R11,1
  @loop_y:
        LEA     R12,[4*R10]
        LEA     R12,[4*R12+R11]
        LEA     RBX,[R12+aPole]
        MOV     AL,[RBX]
        CMP     AL,1
        JNE     @not_checker
        // up
        MOV     AL,[RBX-1]
        CMP     AL,1
        JNE     @skip_up
        MOV     AL,[RBX-2]
        CMP     AL,0
        JNE     @skip_up
        MOV     byte ptr [RBX],0
        MOV     byte ptr [RBX-1],0
        MOV     byte ptr [RBX-2],1
        LEA     RAX,[2*aSC+aSteps]
        MOV     byte ptr [RAX],R12b
        MOV     byte ptr [RAX+1],0
        INC     aSC
        PUSH    RBX
        PUSH    R10
        PUSH    R11
        PUSH    R12
        CALL    @PW
        POP     R12
        POP     R11
        POP     R10
        POP     RBX
        CMP     AL,1
        JE      @win
        MOV     byte ptr [RBX-2],0
        MOV     byte ptr [RBX-1],1
        MOV     byte ptr [RBX],1
        DEC     aSC
  @skip_up:
        // dn
        MOV     AL,[RBX+1]
        CMP     AL,1
        JNE     @skip_dn
        MOV     AL,[RBX+2]
        CMP     AL,0
        JNE     @skip_dn
        MOV     byte ptr [RBX],0
        MOV     byte ptr [RBX+1],0
        MOV     byte ptr [RBX+2],1
        LEA     RAX,[2*aSC+aSteps]
        MOV     byte ptr [RAX],R12b
        MOV     byte ptr [RAX+1],1
        INC     aSC
        PUSH    RBX
        PUSH    R10
        PUSH    R11
        PUSH    R12
        CALL    @PW
        POP     R12
        POP     R11
        POP     R10
        POP     RBX
        CMP     AL,1
        JE      @win
        MOV     byte ptr [RBX+2],0
        MOV     byte ptr [RBX+1],1
        MOV     byte ptr [RBX],1
        DEC     aSC
  @skip_dn:
        // lt
        MOV     AL,[RBX-16]
        CMP     AL,1
        JNE     @skip_lt
        MOV     AL,[RBX-32]
        CMP     AL,0
        JNE     @skip_lt
        MOV     byte ptr [RBX],0
        MOV     byte ptr [RBX-16],0
        MOV     byte ptr [RBX-32],1
        LEA     RAX,[2*aSC+aSteps]
        MOV     byte ptr [RAX],R12b
        MOV     byte ptr [RAX+1],2
        INC     aSC
        PUSH    RBX
        PUSH    R10
        PUSH    R11
        PUSH    R12
        CALL    @PW
        POP     R12
        POP     R11
        POP     R10
        POP     RBX
        CMP     AL,1
        JE      @win
        MOV     byte ptr [RBX-32],0
        MOV     byte ptr [RBX-16],1
        MOV     byte ptr [RBX],1
        DEC     aSC
  @skip_lt:
        // rt
        MOV     AL,[RBX+16]
        CMP     AL,1
        JNE     @skip_rt
        MOV     AL,[RBX+32]
        CMP     AL,0
        JNE     @skip_rt
        MOV     byte ptr [RBX],0
        MOV     byte ptr [RBX+16],0
        MOV     byte ptr [RBX+32],1
        LEA     RAX,[2*aSC+aSteps]
        MOV     byte ptr [RAX],R12b
        MOV     byte ptr [RAX+1],3
        INC     aSC
        PUSH    RBX
        PUSH    R10
        PUSH    R11
        PUSH    R12
        CALL    @PW
        POP     R12
        POP     R11
        POP     R10
        POP     RBX
        CMP     AL,1
        JE      @win
        MOV     byte ptr [RBX+32],0
        MOV     byte ptr [RBX+16],1
        MOV     byte ptr [RBX],1
        DEC     aSC
  @skip_rt:
  @not_checker:
        INC     R11
        CMP     R11,14
        JBE     @loop_y
        INC     R10
        CMP     R10,14
        JBE     @loop_x
        CMP     aSC,aWS
        SETE    AL
  @win:
        RET
  @finish:
        POP     R12
        POP     RBX
end;
Вызвать PoleWorkAsm из консольной программы можно так:
Delphi
1
res:=PoleWorkAsm(StepsCount,WinSteps,addr(Pole[0,0]),addr(Steps[0]));
Разница по скорости на поле из 17 фишек:
PoleWork - 125-130 секунд
PoleWorkAsm - 93-96 секунд
Метки delphi
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 1
Комментарии
  1. Старый комментарий
    Аватар для Hadros

    Не по теме:

    комментарий не удаляется(

    Запись от Hadros размещена 18.11.2015 в 00:59 Hadros вне форума
 
Новые блоги и статьи
Nekobox - outbounds[0].transport: unknown transport type: raw
damix 01.10.2026
Фикс ошибки Правым кликом по серверу -> отладочная информация -> edit Заменить "net": "raw", на "net": "tcp", Нажать кнопку reload.
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru