알려진 대기열로 경기 게임을 최적화하기위한 알고리즘


10

Flowerz로 알려진 게임에 대한 C # .NET에서 솔버를 작성하려고합니다. 참고로 http://zone.msn.com/gameplayer/gameplayer.aspx?game=flowerz 에서 MSN에서 재생할 수 있습니다 . 나는 어떤 종류의 과제 나 일과 관련된 것이 아니라 재미있게 쓰고 있습니다. 이 때문에 유일한 제한은 내 컴퓨터 (8GB RAM이 장착 된 인텔 i7 코어)입니다. 내가 염려하는 한 다른 곳에서는 실행할 필요가 없습니다.

요컨대, 규칙은 다음과 같습니다.

  • 색깔의 꽃으로 가득한 줄이 있습니다. 길이는 임의적입니다
    • 대기열에 영향을 줄 수 없습니다
    • 큐는 레벨 시작시 생성됩니다
  • 꽃은 하나 또는 두 가지 색상이 있습니다.
    • 두 가지 색상이 있으면 외부 색상과 내부 색상이 있습니다. 두 가지 색상의 경우 외부 색상이 일치하는 데 사용됩니다.
    • 일치하는 경우 외부 색상이 사라지고 꽃은 이제 내부 꽃과 동일한 색상의 단색 꽃입니다.
  • 게임의 목표는 같은 색의 3 개 이상의 매치를 만드는 것입니다.
    • 단색의 꽃이 일치하는 부분 인 경우 경기장에서 제거되어 빈 공간을 만듭니다.
    • 단일 색상의 꽃을 2 색의 꽃의 외부 색상과 일치시킬 수 있습니다. 이 경우 단색 꽃이 사라지고 2 색 꽃의 외곽 색이 사라지고 안쪽 색이 유지됩니다.
  • 대기열이 비어 있고 빈 공간이 하나 이상 남아 있으면 라운드에서 승리합니다.
  • 계단식 일치가 가능합니다. 계단식은 3 개 이상의 외부 꽃이 사라지고 내부 색상이 3 (또는 그 이상의 꽃)의 다른 사슬을 형성 할 때입니다.
  • 경기장은 항상 7x7입니다
  • 들판의 일부 공간은 바위로 덮여 있습니다
    • 바위에 꽃을 놓을 수 없습니다
  • 대기열에는 배치 된 꽃을 빈 공간으로 이동하는 데 사용할 수있는 스페이드도 포함될 수 있습니다.
    • 스페이드를 사용해야하지만 실제로 꽃을 움직일 필요는 없습니다. 꽃을 원래 위치에서 바로 다시 배치하는 것이 합법적입니다.
  • 대기열에는 또한 컬러 나비가 포함될 수 있습니다. 이 나비를 꽃에 사용하면 꽃이 나비의 색을 얻습니다.
    • 두 가지 색상의 꽃에 나비를 적용하면 꽃이 하나의 색상, 즉 나비의 색상 만 얻습니다.
    • 빈 공간이나 이미이 색을 가진 꽃에 나비를 낭비 할 수 있습니다
  • 필드를 클리어해도 게임에서이기는 것은 아닙니다

솔버의 목표는 간단합니다. 경기장에 남은 공간을 가능한 많이두고 대기열을 비울 수있는 방법을 찾으십시오. 기본적으로 AI는 게임을합니다. 솔버의 출력은 찾은 움직임이있는 목록입니다. 나는 점수에 관심이 없지만 가능한 한 오래 살아 남기 때문에 가능한 한 많은 열린 공간을 떠나는 움직임에 관심이 있습니다.

말할 필요도없이, 검색 공간은 대기열이 커질수록 빠르게 증가하므로 무차별적인 힘은 문제가되지 않습니다. 대기열은 15에서 시작하여 내가 올바르게 기억하면 2 ~ 3 레벨마다 5로 증가합니다. 물론 첫 번째 꽃을 (0,0)에 배치하고 두 번째 꽃을 (0,1)에 배치하는 것은 특히 첫 번째 꽃을 (1,0)에 배치하고 두 번째 꽃을 (0,0)에 배치하는 것과 다릅니다 필드는 이미 이전 라운드의 꽃으로 채워져 있습니다. 그러한 간단한 결정은 결정에 차이를 만들 수 있습니다.

내가 가진 질문은 다음과 같습니다.

  • 어떤 종류의 문제입니까? (여행사, 배낭 또는 기타 조합 문제를 생각해보십시오). 이것을 아는 것은 내 Google fu를 조금 더 좋게 만들 수 있습니다.
  • 어떤 알고리즘을 사용하면 좋은 결과를 얻을 수 있습니까?

후자에 관해서 : 처음에, 나는 내 자신의 휴리스틱 알고리즘을 작성하려고 시도했지만 (기본적으로 큐를 알고 있다면 어떻게 해결할 것입니까?), 그로 인해 많은 경우와 점수 일치가 누락 될 수 있습니다.

나는 적어도 알고리즘을 사용하는 방법을 알고 있기 때문에 유전자 알고리즘을 사용하려고 생각했지만 보드의 이진 표현을 결정하는 데 문제가 있습니다. 그런 다음 교차 문제가 있지만 순서가 지정된 교차 연산자 또는 유사한 유형의 작업으로 해결할 수 있습니다.

내 생각에 솔버는 항상 보드 구성과 비우려고하는 대기열을 알아야합니다.

신경망 및 퍼지 논리 시스템과 같은 다른 휴리스틱 알고리즘에 대해 알고 있지만 어느 것이 가장 적합한 지 또는 현재 수행중인 작업에 더 적합한 알고리즘이 있는지 알 수있는 경험이 부족합니다.


한 번 내가 작업했던 복잡한 게임의 검색 공간이 32Gb라는 것을 알아 냈습니다. 그 당시에는 불가능했던 20MB 디스크 드라이브가 있었지만 요즘에는 일부 컴퓨터의 RAM에서 거의 가능합니다.
Jonathan

일치하는 색상이 하나만있는 꽃이 완전히 사라 집니까? 두 가지 색상의 꽃이 단일 색상의 단일 색상에 대해 외부 레이어와 일치 할 수 있습니까? 나는 두 가지 모두에 대해 그렇게 추정하지만, 이것들은 문제 설명에 명시 적으로 명시되어 있지 않다 ...
Steven Stadnicki

@StevenStadnicki 감사합니다! 그 정보를 원래 질문에 추가했습니다.
user849924

1
작은 메모로서, 우연히도,이 문제의 '부울'버전 (보드를 끝에서 완전히 비워두기 위해 꽃을 줄에 넣는 방법이 있습니까?)은 NP- 완료일 가능성이 높습니다. NP 완료된 Clickomania 문제 ( erikdemaine.org/clickomania ) 와 명백한 유사점을 지니고 있으며, 다항식 길이의 추출 된 솔루션이 주어지기 때문에 시뮬레이션을 실행하는 것만으로 쉽게 확인할 수 있기 때문에 문제는 NP보다 어렵지 않습니다. 이는 최적화 문제가 아마도 FP ^ NP에 있음을 의미합니다.
Steven Stadnicki

답변:


9

언뜻보기 에 이것은 단일 에이전트 검색 문제인 것 같습니다 . 즉, 하나의 에이전트 (AI "플레이어")가 있습니다. 게임 보드와 대기열의 상태를 나타내는 게임 상태 가 있으며 주어진 상태에서 새로운 상태를 생성 할 수 있는 후속 기능 이 있습니다.

상태가 "해결 된"상태 인시기를 알려주 는 목표 기준 도 있습니다 . 그리고 경로 비용 -주어진 상태로 진행하는 비용 (이 경우 항상 "1 이동").

이런 종류의 프로토 타입 퍼즐 중 하나는 15 퍼즐 입니다. 이를 해결하는 일반적인 방법은 정보를 기반으로 한 검색 ( 예 : 클래식 휴리스틱 검색 A * 및 변형)을 사용하는 것입니다.


그러나이 첫눈에 접근하는 데 문제가 있습니다. A *와 같은 알고리즘은 목표에 가장 짧은 경로를 제공하도록 설계되었습니다 (예 : 가장 적은 수의 이동). 귀하의 경우, 이동의 수는 항상 고정되어 - 더 짧은 경로가 없습니다 - 휴리스틱 검색이 당신에게 줄 것이다 있도록 에 경로를 완료 게임.

원하는 것은 최고의 게임 상태 를 제공하는 일련의 동작입니다 .

그래서 당신이해야 할 일은 약간의 문제를 돌리는 것입니다. 게임 보드가 "상태" 가 아닌 이동 순서 가 "상태"가됩니다. (즉 : 항목을 대기열에 "D2, A5, C7, B3, A3, ..."위치)

즉, 이러한 상태가 생성되는 방식에 실제로 신경 쓰지 않습니다. 보드 자체는 부수적이며 주어진 상태의 품질을 평가하기 위해서만 필요합니다.

이것은 문제를 최적화 문제 로 바꾸는데, 이것은 로컬 검색 알고리즘 으로 해결할 수 있습니다 (기본적으로 주어진 상태 주변의 상태를 만들고 상태 간의 경로를 신경 쓰지 않고 최상의 상태를 선택하는 것을 의미합니다).

이런 종류 의 원형 퍼즐은 여덟 여왕 퍼즐 입니다.

이 문제 클래스에서 상태 공간을 검색하여 "좋은"은 목적 함수 ( 평가 함수 또는 유전자 알고리즘의 경우 적합 함수 )에 의해 평가되는 적절한 솔루션을 찾습니다 .

문제의 경우, 목적 함수는 실패 상태에 도달하기 전에 사용 된 큐의 항목 수에 대해 0과 N 사이의 값을 리턴 할 수 있습니다 (여기서 N은 큐 길이). 그렇지 않은 경우 N + M 값. 여기서 M은 큐가 비어있는 후 보드에 남아있는 빈 공간 수입니다. 따라서 값이 높을수록 솔루션이 "객관적으로 향상됩니다".

(이 시점에서 게임을 실행하는 코드에서 크랩을 최적화해야한다는 점에 주목할 필요가 있습니다.이 상태는 목적 함수에 사용할 수있는 완성 된 보드로 바뀝니다.)


로컬 검색 알고리즘의 예 : 기본 패턴은 특정 상태를 취하고이를 변경하며 다음 상태로 이동하여 더 나은 결과를 제공하는 언덕 등반 검색입니다.

분명히 이것은 로컬 최대 값 등에서 멈출 수 있습니다. 이 형식에서는 탐욕스러운 지역 검색 이라고합니다 . 이 문제와 다른 문제를 다루는 변형이 많이 있습니다 ( Wikipedia에서 다뤘습니다 ). 그 중 일부 (예 : 로컬 빔 검색 )는 여러 상태를 한 번에 추적합니다.

이것에 대한 하나의 특별한 변형은 유전자 알고리즘 ( Wikipedia )이다. 유전자 알고리즘의 기본 단계는 다음과 같습니다.

  1. 상태를 어떤 종류의 문자열로 변환하는 방법을 결정하십시오. 귀하의 경우 이것은 1에서 49까지의 대기열 길이 자릿수 일 수 있습니다 (7x7 보드의 가능한 모든 배치를 나타냅니다. 각각 1 바이트 저장). "스페이드"조각은 각 이동 단계마다 두 개의 후속 대기열 항목으로 표시 될 수 있습니다.
  2. 번식 인구를 무작위로 선택하여 체력 이 더 좋은 주에 확률을 높 입니다. 번식 개체 수는 원래 개체 수와 크기가 같아야합니다. 원래 개체 수에서 여러 번 상태를 선택할 수 있습니다.
  3. 번식 개체군의 짝짓기 상태 (첫 번째는 두 번째, 세 번째는 네 번째 등)
  4. 각 쌍 (문자열의 위치)에 대한 교차점을 임의로 선택 합니다.
  5. 교차점 뒤의 문자열 부분을 교환하여 각 쌍에 대해 두 개의 자손을 만듭니다.
  6. 각 자손 상태를 무작위로 변경합니다. 예를 들어 : 문자열에서 임의의 위치를 ​​임의의 값으로 변경하도록 임의로 선택하십시오.
  7. 모집단이 하나 이상의 솔루션에 수렴 될 때까지 (또는 지정된 수의 세대가 있거나 충분한 솔루션이 발견 된 경우) 새 모집단에 대해 프로세스를 반복하십시오.

이 같은 유전자 알고리즘 솔루션은 느낌 수도 문제에 적합 - 약간의 조정과 함께. 내가 본 가장 큰 어려움은 위의 문자열 표현으로 매우 다른 전면 반으로 상태의 꼬리 반쪽을 전환하면 "두 반쪽 사이의 충돌하는 움직임으로 인해"죽은 "상태가 될 수 있다는 것입니다. 낮은 체력 점수).

아마도이 문제를 극복 할 수 있습니다. 마음에 떠오르는 한 가지 아이디어는 앞쪽 절반이 비슷한 주들이 번식 쌍이 될 가능성을 높이는 것입니다. 이것은 번식하기 전에 번식 인구를 분류하는 것만 큼 간단 할 수 있습니다. 또한 생성 수가 증가함에 따라 크로스 오버의 가능한 위치를 줄의 시작에서 끝으로 점차 이동하는 데 도움이 될 수 있습니다.

"평방이 가득 참"고장 상태에 직면하는 것에 대해 더 저항적인 상태 (아마도 완전히 면역적인 상태) 내에서의 움직임의 표현을 생각해내는 것도 가능할 수있다. 아마도 움직임을 이전 움직임의 상대 좌표로 나타낼 수 있습니다. 또는 이동하면 주어진 위치에 가장 가까운 빈 공간을 선택합니다.

이와 같은 사소한 AI 문제와 마찬가지로 중대한 문제가 발생할 수 있습니다.

앞에서 언급했듯이 다른 주요 과제는 단순히 목표 기능을 최적화하는 것입니다. 이 속도를 높이면 대량의 공간을 검색하고 대기열이 더 긴 게임에 대한 솔루션을 검색 할 수 있습니다.


이 해답을 위해, 특히 모든 용어를 올바르게 얻으려면 Russell과 Norvig의 대학 인공 지능 교과서 "인공 지능 : 현대적 접근 방식"을 찾아야했습니다. 그것이 "좋은"것인지 (나는 그것을 비교할 다른 AI 텍스트가 없다) 확실하지 않지만 나쁘지 않습니다. 적어도 그것은 꽤 큽니다.)


크로스 오버 문제도 확인했습니다. 어린이가 대기열에 사용할 수있는 것보다 많은 항목을 가지고있을 가능성이 매우 높습니다 (TSP의 GA가 부족합니다 : 도시를 두 번 이상 방문하거나 전혀하지 않을 수도 있습니다!). 정렬 된 크로스 오버 ( permutationcity.co.uk/projects/mutants/tsp.html )가 작동 할 수 있습니다. 이것은 이동 순서를 상태로 만들 때 특히 적용됩니다.
user849924

그것이 옳다는 것을 확신하지 못합니다-제 생각에, 실패 상태는 조각이 이미 점령 된 위치에 놓이는 것입니다 (따라서 게임을 일찍 끝내서 체력이 낮습니다). 따라서 대기열 길이는 유전자 스트링의 길이와 일치합니다. 결코 잘못된 길이가 아닙니다. 아직도-당신은 교환과 주문이라는 아이디어로 뭔가있을 수 있습니다. 주어진 주문으로 인해 게임이 완료되고 두 번의 움직임을 바꾸면, 단순히 한 (또는 두?) 움직임의 위치를 ​​무작위로 설정하는 것보다 돌연변이 된 상태가 완료된 게임 일 가능성이 훨씬 높다고 생각합니다. .
Andrew Russell

실패 상태는 더 이상 이동할 수있는 옵션이없는 경우, 즉 빈 공간이 부족하고 해당 이동과 일치하지 않는 경우입니다. 당신이 말하는 것과 비슷합니다 : 당신은 이미 점령 된 위치에 그것을 배치해야합니다 (그러나 더 이상 시작할 곳이 없을 때만 사실입니다). 내가 게시 한 크로스 오버는 흥미로울 수 있습니다. 염색체 A에는 A1, B1, ..., G1, A2, B2 및 C2에 항목이 있고 G7 ... A7, G6, F6 및 E6에 염색체 B가 있습니다. A에서 무작위를 선택하고 지수를 유지하십시오. B에서 A의 보수를 선택하고 인덱스를 유지하고 어린이를 위해 병합하십시오.
user849924 2016 년

이 크로스 오버의 '문제'는 같은 지점에서 여러 번의 이동이 허용된다는 것입니다. 그러나 그것은 Stefan K의 솔루션의 SimulateAutomaticChanges와 비슷한 것으로 쉽게 해결할 수 있어야합니다 : 어린이의 이동 세트 / 상태를 경기장의 기본 상태 (단순히 모든 이동을 하나씩 적용)와 수용 상태 (빈 큐) )에 도달 할 수 없습니다 (점유 지점에 꽃을 놓아야하기 때문에). 그 아이는 유효하지 않으므로 다시 번식해야합니다. 여기에 고장 상태가 나타납니다. 나는 지금 그것을 얻는다. : D
user849924 2016 년

나는 이것을 두 가지 이유로 대답으로 받아들입니다. 첫째 : GA에서이 문제를 해결하는 데 필요한 아이디어를 알려 주셨습니다. 둘째 : 당신은 처음이었습니다. ; p
user849924

2

분류

대답은 쉽지 않습니다. 게임 이론에는 게임에 대한 분류가 있지만, 해당 게임과 특별한 이론이 1 : 1로 일치하지 않는 것 같습니다. 특별한 형태의 조합 문제입니다.

세일즈맨은 여행하지 않으며, 마지막 노드에서 다음 노드에 도달하는 데 약간의 비용이 "노드"를 방문하는 순서를 결정합니다. 대기열을 다시 정렬 할 수 없으며지도에서 모든 필드를 사용해야 할 수도 있습니다.

일부 항목을 "백팩"에 넣는 동안 일부 필드가 비어 있기 때문에 배낭이 일치하지 않습니다. 확장 된 형태 일 수도 있지만, 이로 인해 알고리즘을 적용 할 수 없을 것입니다.

Wikipedia는 분류에 대한 힌트를 제공합니다 : http://en.wikipedia.org/wiki/Game_theory#Types_of_games

나는 이것을 "이산 시간 최적의 제어 문제"( http://en.wikipedia.org/wiki/Optimal_control )로 분류하지만 이것이 도움이 될 것이라고 생각하지 않습니다.

알고리즘

전체 대기열을 실제로 알고있는 경우 트리 검색 알고리즘을 적용 할 수 있습니다. 당신이 말했듯이, 문제의 복잡성은 대기열 길이에 따라 매우 빠르게 증가합니다. 많은 메모리를 필요로하지 않는 "DFS (Depth-first search)"와 같은 알고리즘을 사용하는 것이 좋습니다. 점수가 중요하지 않기 때문에 첫 번째 해결책을 찾은 후에 멈출 수 있습니다. 먼저 검색 할 하위 브랜치를 결정하려면 주문에 대해 휴리스틱을 적용해야합니다. 즉, 평가 필드를 작성해야합니다 (예 : 빈 필드 수,이 필드가 더 정교할수록 더 우수). 다음으로 예상되는 움직임이 가장 유망합니다.

그런 다음 다음 부분 만 필요합니다.

  1. 게임의 모든 정보를 저장하는 게임 상태 모델 (예 : 보드 상태 /지도, 대기열, 대기열에서 이동 번호 / 위치)
  2. 주어진 게임 상태에 대한 모든 유효한 이동을 제공하는 이동 생성기
  3. "이동"및 "실행 취소"기능; 주어진 (유효한) 이동을 게임 상태로 적용 / 실행 취소합니다. "이동"기능은 "실행 취소"기능에 대한 "실행 취소 정보"를 저장해야합니다. 게임 상태를 복사하고 반복 할 때마다 수정하면 검색 속도가 크게 느려집니다! 최소한 스택에 상태를 저장하십시오 (= 로컬 변수, "new"를 사용한 동적 할당 없음).
  4. 각 게임 상태에 대해 비슷한 점수를주는 평가 기능
  5. 검색 기능

깊이 우선 검색을위한 불완전한 참조 구현은 다음과 같습니다.

public class Item
{
    // TODO... represents queue items (FLOWER, SHOVEL, BUTTERFLY)
}

public class Field
{
    // TODO... represents field on the board (EMPTY or FLOWER)
}

public class Modification {
    int x, y;
    Field originalValue, newValue;

    public Modification(int x, int y, Field originalValue, newValue) {
        this.x = x;
        this.y = y;
        this.originalValue = originalValue;
        this.newValue = newValue;
    }

    public void Do(GameState state) {
        state.board[x,y] = newValue;
    }

    public void Undo(GameState state) {
        state.board[x,y] = originalValue;
    }
}

class Move : ICompareable {

    // score; from evaluation function
    public int score; 

    // List of modifications to do/undo to execute the move or to undo it
    Modification[] modifications;

    // Information for later knowing, what "control" action has been chosen
    public int x, y;   // target field chosen
    public int x2, y2; // secondary target field chosen (e.g. if moving a field)


    public Move(GameState state, Modification[] modifications, int score, int x, int y, int x2 = -1, int y2 = -1) {
        this.modifications = modifications;
        this.score = score;
        this.x = x;
        this.y = y;
        this.x2 = x2;
        this.y2 = y2;
    }

    public int CompareTo(Move other)
    {
        return other.score - this.score; // less than 0, if "this" precededs "other"...
    }

    public virtual void Do(GameState state)
    {
        foreach(Modification m in modifications) m.Do(state);
        state.queueindex++;
    }

    public virtual void Undo(GameState state)
    {
        --state.queueindex;
        for (int i = m.length - 1; i >= 0; --i) m.Undo(state); // undo modification in reversed order
    }
}

class GameState {
    public Item[] queue;
    public Field[][] board;
    public int queueindex;

    public GameState(Field[][] board, Item[] queue) {
        this.board = board;
        this.queue = queue;
        this.queueindex = 0;
    }

    private int Evaluate()
    {
        int value = 0;
        // TODO: Calculate some reasonable value for the game state...

        return value;
    }

    private List<Modification> SimulateAutomaticChanges(ref int score) {
        List<Modification> modifications = new List<Modification>();
        // TODO: estimate all "remove" flowers or recoler them according to game rules 
        // and store all changes into modifications...
        if (modifications.Count() > 0) {
            foreach(Modification modification in modifications) modification.Do(this);

            // Recursively call this function, for cases of chain reactions...
            List<Modification> moreModifications = SimulateAutomaticChanges();

            foreach(Modification modification in modifications) modification.Undo(this);

            // Add recursively generated moves...
            modifications.AddRange(moreModifications);
        } else {
            score = Evaluate();
        }

        return modifications;
    }

    // Helper function for move generator...
    private void MoveListAdd(List<Move> movelist, List<Modifications> modifications, int x, int y, int x2 = -1, int y2 = -1) {
        foreach(Modification modification in modifications) modification.Do(this);

        int score;
        List<Modification> autoChanges = SimulateAutomaticChanges(score);

        foreach(Modification modification in modifications) modification.Undo(this);

        modifications.AddRange(autoChanges);

        movelist.Add(new Move(this, modifications, score, x, y, x2, y2));
    }


    private List<Move> getValidMoves() {
        List<Move> movelist = new List<Move>();
        Item nextItem = queue[queueindex];
        const int MAX = board.length * board[0].length + 2;

        if (nextItem.ItemType == Item.SHOVEL)
        {

            for (int x = 0; x < board.length; ++x)
            {
                for (int y = 0; y < board[x].length; ++y)
                {
                    // TODO: Check if valid, else "continue;"

                    for (int x2 = 0; x2 < board.length; ++x2)
                    {
                        for(int y2 = 0; y2 < board[x].length; ++y2) {
                            List<Modifications> modifications = new List<Modifications>();

                            Item fromItem = board[x][y];
                            Item toItem = board[x2][y2];
                            modifications.Add(new Modification(x, y, fromItem, Item.NONE));
                            modifications.Add(new Modification(x2, y2, toItem, fromItem));

                            MoveListAdd(movelist, modifications, x, y, x2, y2);
                        }
                    }
                }
            }

        } else {

            for (int x = 0; x < board.length; ++x)
            {
                for (int y = 0; y < board[x].length; ++y)
                {
                    // TODO: check if nextItem may be applied here... if not "continue;"

                    List<Modifications> modifications = new List<Modifications>();
                    if (nextItem.ItemType == Item.FLOWER) {
                        // TODO: generate modifications for putting flower at x,y
                    } else {
                        // TODO: generate modifications for putting butterfly "nextItem" at x,y
                    }

                    MoveListAdd(movelist, modifications, x, y);
                }
            }
        }

        // Sort movelist...
        movelist.Sort();

        return movelist;
    }


    public List<Move> Search()
    {
        List<Move> validmoves = getValidMoves();

        foreach(Move move in validmoves) {
            move.Do(this);
            List<Move> solution = Search();
            if (solution != null)
            {
                solution.Prepend(move);
                return solution;
            }
            move.Undo(this);
        }

        // return "null" as no solution was found in this branch...
        // this will also happen if validmoves == empty (e.g. lost game)
        return null;
    }
}

이 코드는 작동하지 않으며 컴파일 가능하거나 완료되지 않았습니다. 그러나 그것을 수행하는 방법에 대한 아이디어를 제공해야합니다. 가장 중요한 작업은 평가 기능입니다. 더 정교할수록 알고리즘이 나중에 시도하고 실행 취소해야하는 "시도"가 잘못됩니다. 이것은 복잡성을 크게 줄입니다.

이것이 너무 느리면 HashTables로 두 사람 게임의 일부 방법을 적용 할 수도 있습니다. 이를 위해서는 평가하는 각 게임 상태에 대해 (반복적) 해시 키를 계산하고 솔루션으로 이어지지 않는 상태를 표시해야합니다. 예를 들어 Search () 메소드가 "null"을 리턴하기 전에는 항상 HashTable 항목을 작성해야하며 Search ()를 입력 할 때이 상태에 아직 긍정적 인 결과가없는 상태에 도달했는지 확인하고 그렇지 않은 경우 "null"을 리턴합니다. 추가 조사. 이를 위해서는 거대한 해시 테이블이 필요하며 "해시 충돌"을 받아 들여야합니다. 이로 인해 기존 솔루션을 찾지 못할 수도 있지만 해시 함수가 충분하고 테이블이 충분히 큰 (계산 가능한 위험의 위험).

더 효율적 으로이 문제를 해결할 수있는 다른 알고리즘은 없다고 생각합니다. 평가 기능이 최적이라고 가정합니다 ...


예, 전체 대기열을 알 수 있습니다. 평가 기능의 구현이 유효하지만 잠재적으로 나쁜 배치를 고려할 것입니까? 비슷한 색상이 필드에있을 때 다른 색상의 꽃 옆에 배치하는 것과 같은 움직임이 잠재적으로 나쁜가? 아니면 공간이 부족하여 완전히 다른 블록을 가진 어딘가에 꽃을 놓는가?
user849924 2016 년

이 답변은 모델에 대한 아이디어와 게임 규칙을 다루는 방법을 알려 주었으므로이를 찬성하겠습니다. 입력 해 주셔서 감사합니다!
user849924

@ user849924 : 예, 물론 평가 기능은 이에 대한 평가 "값"을 계산해야합니다. 현재 게임 상태가 잃을수록 악화 될수록 (반환 손실) 반환 된 평가 값이 더 나빠집니다. 가장 쉬운 평가는 빈 필드 수를 반환하는 것입니다. 비슷한 색상의 꽃 옆에 배치 된 각 꽃에 대해 0.1을 추가하여이를 개선 할 수 있습니다. 함수가 임의의 게임 상태를 선택하고 값을 계산 한 후 비교하십시오. 만약 상태 A가 상태 B보다 낫다고 생각한다면, 점수 A는 B보다 우수해야합니다.
SDwarfs
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.