PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » PureBasic для Windows » Алгоритм хочу


Алгоритм хочу

Сообщений 1 страница 4 из 4

1

Сомневаюсь, что это кого-нибудь заинтересует, но все-таки спрошу.
Решил я тут на днях сделать простенький клиент для KataGo (мощный движок для игры в го с возможностью анализа)
и столкнулся с задачей определения мертвых групп. Когда-то, очень давно, я успешно эту задачу решал на BlitzBasic (тогда еще не было PB).
Но с тех пор и код был утерян, и тот скромный навык который был, и сообразительность. И теперь не могу допетрить - как я это делал.
Потому тупо спросил у Алисы...
На удивление она выдала почти рабочий код, который я здесь привожу с некоторыми исправлениями и дополнениями.
Это работает, но сам алгоритм какой-то, скажем так, слишком трудоемкий.
Да, можно сделать так, чтобы анализ запускался  только при контактном ходе с камнем другого цвета (а не после каждого хода),
но этого мало. Нужен другой алгоритм - я ведь делал это как-то иначе (и без рекурсии, и без списков, и без необходимости проверять
после каждого хода ВСЕ группы на доске).
Может кто натолкнет на мысль?

Код:
Global Dim board(18, 18), Dim visited(18, 18), col=1 ; col=1 - черные камни, col=2 - белые

Structure StoneGroup ; для координат камней в группе
  x.i
  y.i
EndStructure

Procedure FindGroup(x, y, color, List group.StoneGroup()) ; Поиск группы
  If x < 0 Or x >18 Or y < 0 Or y > 18  ; Проверка границ доски
    ProcedureReturn
  EndIf  
  If visited(x, y) Or board(x, y) <> color ; Если клетка уже посещена или цвет не совпадает — выход
    ProcedureReturn
  EndIf
  visited(x, y) = 1  ; Помечаем как посещённую
  AddElement(group()) ; Добавляем камень в группу
  group()\x = x
  group()\y = y
  ; Рекурсивно обходим соседей (4 направления)
  FindGroup(x - 1, y, color, group())  ; влево
  FindGroup(x + 1, y, color, group())  ; вправо
  FindGroup(x, y - 1, color, group())  ; вверх
  FindGroup(x, y + 1, color, group())  ; вниз
EndProcedure

Procedure Liberty(List group.StoneGroup()) ; Есть ли дамэ (свободный пункт) у группы?
  ForEach group()
    x = group()\x
    y = group()\y
    ; Проверяем 4 соседних пункта
    If x > 0 And board(x - 1, y) = 0 : ProcedureReturn 1 : EndIf
    If x < 18 And board(x + 1, y) = 0 : ProcedureReturn 1 : EndIf
    If y > 0 And board(x, y - 1) = 0 : ProcedureReturn 1 : EndIf
    If y < 18 And board(x, y + 1) = 0 : ProcedureReturn 1 : EndIf
  Next
  ProcedureReturn 0  ; Нет ни одного дамэ
EndProcedure

Procedure RemoveDead(targetColor) ; Удаление мёртвых групп заданного цвета
  For x = 0 To 18  ; Сбрасываем флаги посещения
    For y = 0 To 18
      visited(x, y) = 0
    Next
  Next
  For x = 0 To 18  ; Проходим по всем клеткам доски
    For y = 0 To 18
      If board(x, y) = targetColor And Not visited(x, y)  ; Камень нужного цвета и не посещён
        NewList group.StoneGroup()
        FindGroup(x, y, targetColor, group())
        If Not Liberty(group())  ; Группа мёртвая — удаляем
          Debug "Удалена группа (" + Str(x) + "," + Str(y) + ") (цвет: " + Str(targetColor) + ")"
          ForEach group()
            board(group()\x, group()\y) = 0  ; Очищаем клетку
          Next
        EndIf
        ClearList(group())
      EndIf
    Next
  Next
EndProcedure

Procedure PrintBoard()   ; Вывод доски (для отладки)
  StartDrawing(CanvasOutput(1))
  DrawImage(ImageID(0), 0, 0)
  For y = 0 To 18
    For x = 0 To 18
      If board(x, y)
        DrawAlphaImage(ImageID(board(x, y)), x*39, y*39)
      EndIf
    Next
  Next
  StopDrawing()
EndProcedure

Procedure clik() ; Левый клик на доске
  x=GetGadgetAttribute(1, #PB_Canvas_MouseX)/39
  y=GetGadgetAttribute(1, #PB_Canvas_MouseY)/39
  If x<19 And y<19 And board(x,y)=0
    board(x,y)=col
    RemoveDead(3-col)
    PrintBoard()
    col=3-col
  EndIf
EndProcedure

Procedure risunok() ; Для визуализации работы алгоритма
  StartDrawing(ImageOutput(0))
  For i=21 To 741 Step 39
    LineXY(21, i, 723, i, 0) : LineXY(i, 21, i, 723, 0)
  Next
  DrawingMode(#PB_2DDrawing_Outlined) : Box(20, 20, 705, 705, 0)
  StopDrawing()
  For i=1 To 2
    StartDrawing(ImageOutput(i))
    DrawingMode(#PB_2DDrawing_Outlined | #PB_2DDrawing_AlphaBlend)
    Circle(21, 21, 18, RGBA(5, 5, 5, 255))
    FillArea(21, 21, RGB(5, 5, 5), RGBA(60*i*i, 60*i*i, 60*i*i, 255))
    StopDrawing()
  Next
  SetGadgetAttribute(1,#PB_Canvas_Image,ImageID(0))
EndProcedure

; ---------- ОСНОВНАЯ ПРОГРАММА ----------
OpenWindow(0, 0, 0, 756, 756, "Algorithm", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
SetWindowColor(0, $6699CC)
CanvasGadget(1, 6, 6, 745, 745)
CreateImage(0, 745, 745, 24, $77CEFF)
CreateImage(1, 43, 43 ,32, #PB_Image_Transparent)
CreateImage(2, 43, 43 ,32, #PB_Image_Transparent)

risunok() ; Простенький рисунок доски и камней

Repeat
  Select WaitWindowEvent()
    Case  #PB_Event_CloseWindow
      End
    Case #PB_Event_Gadget
      If EventGadget()=1 And EventType()=#PB_EventType_LeftClick
        clik()
      EndIf
  EndSelect
ForEver

0

2

Лучше приведенного ниже у меня не получилось.

Код:
Global cl=1, hbw, x, y, Dim dsk(18,18), NewList ng(), NewList nud()

Procedure scan(a,b)
  If a<0 Or a>18 Or b<0 Or b>18:ProcedureReturn:EndIf
  If dsk(a,b)
    If (2-cl)*dsk(a,b)>0              ; свой камень
      If dsk(x,y)=0
        dsk(x,y)=dsk(a,b)             ; присваиваем поставленному камню номер своего
      ElseIf dsk(a,b)<>dsk(x,y)
        AddElement(ng()) : ng()=dsk(a,b) ; добавляем номер группы (камня) в список своих
      EndIf
    Else                              ; чужой
      AddElement(nud()) : nud()=dsk(a,b)  ; в список чужих
    EndIf
  EndIf
EndProcedure

Procedure prud(a,b)           ; живой или мертвый камень
  If a>0 And dsk(a-1,b)=0:ProcedureReturn 1:EndIf
  If a<18 And dsk(a+1,b)=0:ProcedureReturn 1:EndIf
  If b>0 And dsk(a,b-1)=0:ProcedureReturn 1:EndIf
  If b<18 And dsk(a,b+1)=0:ProcedureReturn 1:EndIf
  ProcedureReturn 0
EndProcedure

Procedure zamen()
  m=2-cl
  For a=0 To 18 : For b=0 To 18
    If ListSize(ng()) And (dsk(a,b)*m)>0      ; объеденяем в группу своих
      ForEach ng()
        If ng()=dsk(a,b) : dsk(a,b)=dsk(x,y) : EndIf
      Next
    EndIf
    If ListSize(nud()) And dsk(a,b)      ; определяем жизнь-смерть чужих
      ForEach nud()
        If nud()=dsk(a,b) And prud(a,b):DeleteElement(nud()):EndIf
      Next
    EndIf
  Next : Next
EndProcedure

Procedure drwn(t)                 ; вывод на канвас
  StartDrawing(CanvasOutput(1))
  DrawAlphaImage(ImageID(cl), x*39, y*39)
  If t                            ; если есть удаляемые камни
    DrawImage(ImageID(0), 0, 0)
    For a=0 To 18 : For b=0 To 18
      If dsk(a,b)
        ForEach nud():If nud()=dsk(a,b):dsk(a,b)=0:udk+1:EndIf:Next
      EndIf
      If dsk(a,b):DrawAlphaImage(ImageID(2-Sign(dsk(a,b))),a*39,b*39):EndIf
    Next : Next
    Debug "Удалено "+udk+Mid(" черных белых",1+Bool(cl=1)*7,7)+" камней"
  EndIf
  StopDrawing()
EndProcedure

Procedure clik()
  x=GetGadgetAttribute(1, #PB_Canvas_MouseX)/39
  y=GetGadgetAttribute(1, #PB_Canvas_MouseY)/39
  If x<19 And y<19 And dsk(x,y)=0
    drwn(0)
    ClearList(ng()) : ClearList(nud())
    scan(x-1,y) : scan(x,y-1) : scan(x+1,y) : scan(x,y+1)
    If dsk(x,y)=0 : hbw+1 : dsk(x,y)=hbw*(2-cl) : EndIf   ; если нет своих
    If ListSize(ng())+ListSize(nud()) : zamen() : EndIf   ; если есть свои или чужие
    If ListSize(nud()) : drwn(1) : EndIf               ; если есть удаляемые камни
    cl=4-cl         ; меняем цвет
  EndIf
EndProcedure

Procedure risunok() ; Для визуализации работы алгоритма
  StartDrawing(ImageOutput(0))
  For i=21 To 741 Step 39
    LineXY(21, i, 723, i, 0) : LineXY(i, 21, i, 723, 0)
  Next
  DrawingMode(#PB_2DDrawing_Outlined) : Box(20, 20, 705, 705, 0)
  StopDrawing()
  For i=1 To 3 Step 2
    StartDrawing(ImageOutput(i))
    DrawingMode(#PB_2DDrawing_Outlined | #PB_2DDrawing_AlphaBlend)
    Circle(21, 21, 18, RGBA(5, 5, 5, 255))
    FillArea(21, 21, RGB(5, 5, 5), RGBA(60+k, 60+k, 60+k, 255))
    k+180
    StopDrawing()
  Next
  SetGadgetAttribute(1,#PB_Canvas_Image,ImageID(0))
EndProcedure

; ---------- ОСНОВНАЯ ПРОГРАММА ----------
OpenWindow(0, 0, 0, 756, 756, "Algorithm", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
SetWindowColor(0, $6699CC)
CanvasGadget(1, 6, 6, 745, 745)
CreateImage(0, 745, 745, 24, $77CEFF)
CreateImage(1, 43, 43 ,32, #PB_Image_Transparent)
CreateImage(3, 43, 43 ,32, #PB_Image_Transparent)

risunok() ; Простенький рисунок доски и камней

Repeat
  Select WaitWindowEvent()
    Case  #PB_Event_CloseWindow
      End
    Case #PB_Event_Gadget
      If EventGadget()=1 And EventType()=#PB_EventType_LeftClick
        clik()
      EndIf
  EndSelect
ForEver

Нелегальные ходы движок определяет, поэтому здесь они не учитываются.

Отредактировано Andruk (05.01.2026 22:00:47)

0

3

Добрый день, можно узнать, какой ставился вопрос Алисе? Или какая задача.

0

4

Timon написал(а):

Добрый день, можно узнать, какой ставился вопрос Алисе? Или какая задача.

Я точно не помню, но примерно так: Напиши программу на языке PureBasic для определения живых и мёртвых камней в игре Го.

0


Вы здесь » PureBasic - форум » PureBasic для Windows » Алгоритм хочу