Minecraft의 제작 그리드와 같은 것을 어떻게 구현할 수 있습니까?


55

Minecraft제작 시스템은 2x2 또는 3x3 그리드를 사용합니다. 그리드에 재료를 놓고 올바른 재료를 올바른 패턴에 넣으면 레시피가 활성화됩니다.

디자인에 대한 몇 가지 흥미로운 점 :

  • 일부 레시피는 특정 재료를 다른 재료와 교환 할 수 있습니다. 예를 들어, 은 손잡이에 막대기를 사용하고 머리에는 나무 판자, 조약돌, 철 주괴, 금 주괴 또는 다이아몬드 보석을 사용할 수 있습니다.
  • 패턴 내에서의 상대적 위치는 그리드의 절대 위치가 아니라 중요합니다. 즉, 스틱과 석탄 (또는 숯)을 3x3 그리드의 6 개 위치 중 올바른 패턴으로 배치하여 토치 를 만들 수 있습니다 .
  • 패턴이 가로로 뒤집힐 수 있습니다.

나는 그것을 너무나 생각할지도 모르지만 이것은 흥미로운 검색 / 설정 감소 문제처럼 보입니다. 그렇다면 알고리즘 적으로 말하면 어떻게 작동합니까?


6
멋진 질문입니다! 매우 흥미 롭습니다! 이미 몇 가지 아이디어가 있지만 집에 도착하자마자 공유하겠습니다.
jcora

실제로 "가능한 한 닫는대로"마인 크래프트 사본을 작성하려고합니다. 나는 모든 재고 및 레시피 관리자 자료를 이미 작성했으며 매우 깔끔하게 작동한다고 말해야합니다. 깨끗한 코드에 만족합니다. 그러나 작성하기 쉽지 않았습니다. 나는 요리법, 격자, 목록 등을 모두 잘 작동시키기 위해 약 10 개의 수업을 썼습니다. 시간을 찾으면 답을 쓸 것입니다.
Martijn Courteaux

마인 크래프트가 제작법을 어떻게 코딩하는지 살펴 봐야합니다.
Bradman175

답변:


14

또 다른 해결책은 다소 복잡한 트리를 사용하는 것입니다. 트리에서 브랜치 노드는 레시피를 반복하여 생성합니다 (다시 한번 사용 for (y) { for (x) }). 이것은 재고 표준 책별 트리 구조입니다. 최종 노드에는 치수를 레시피에 매핑 하는 추가 구조 ( Dictionary/ HashMap)가 포함됩니다.

본질적으로 당신이하려고하는 것은 이것입니다 :

레시피 트리

검은 색 노드는 항목 유형을 나타내는 브랜치입니다. 빨간색 노드는 크기 / 방향을 구별 할 수있는 나뭇잎 (터미네이터)입니다.

이 트리를 검색하려면 먼저 경계 상자를 찾은 다음 ( 첫 번째 답변에 설명 된 대로) 이동하는 동안 트리를 통과하는 순서와 동일한 순서로 노드를 반복합니다. 마지막으로 단순히 치수를 찾 Dictionary거나 HashMap레시피 결과를 얻습니다.

발차기를 위해 나는 이것을 가서 구현했습니다 . 아마도 내 대답을 분명히 할 것입니다. 또한 : 나는 이것이 다른 대답이라는 것을 알고 있습니다. 정확히 다릅니다 : 그것은 다른 해결책입니다.


매우 간단합니다. 나는 그것을 좋아한다.
David Eyk

나무, 해시 및 다이어그램을 좋아하기 때문에 이것을 받아 들일 것입니다. 다이어그램을 살펴보면 알고리즘이 거의 즉시 이해되었습니다.
David Eyk

23

Minecraft는 매우 작은 가능한 레시피 세트 만 사용하므로 그 어떤 것도 필요하지 않습니다.

그것은 내가 할 일은 맞는 가장 작은 격자를 찾는 것입니다 (즉, 빈 행과 열을 무시하여 2x2 또는 3x3 또는 2x3 (문)인지 확인하십시오). 그런 다음 해당 크기의 레시피 목록을 반복하여 항목 유형이 같은지 확인하십시오 (즉, 항목 및 블록에 정수 유형 ID를 사용하므로 minecraft에서 최악의 9 개의 정수 비교). 일치를 찾으면 중지하십시오.

이 방법은 또한 아이템의 상대적 위치를 무의미하게 만듭니다 (크래프팅 그리드의 어느 곳에 나 토치를 넣을 수 있으며 대부분 비어있는 3x3 상자가 아닌 1x2 상자로 보이므로 작동합니다).

가능한 일치 항목을 통해 선형 검색을 수행하는 데 많은 양의 레시피가있는 경우 목록을 정렬하고 이진 검색 (O (log (N)) 대 O (N))을 수행 할 수 있습니다. 이로 인해 목록을 작성하는 데 약간의 추가 작업이 발생하지만 시작시 한 번 수행하고 나중에 메모리에 유지할 수 있습니다.

또한 레시피를 가장 간단하게 뒤집을 수있게하는 마지막 한 가지 방법은 미러 버전을 목록에 추가하는 것입니다.

두 번째 레시피를 추가하지 않고 입력 레시피를 입력하려는 경우 입력 레시피에 [0,2]보다 ID가 높은 [0,0]의 항목이 있는지 (2x2의 경우 [0,1]인지 확인하지 않아도 됨) 확인할 수 있습니다. 1x2의 경우, 만약 그렇다면, 다음 행을 계속 확인하지 않으면 끝까지 도달 할 수 있습니다.이를 사용하여 레시피가 올바른 회전으로 추가되었는지 확인해야합니다.


1
Minecraft의 소수의 요리법이 선형 검색에 적합하지 않을지 궁금했습니다.
David Eyk

1
그것은 기본적으로 위에서 작성한 것입니다 (이진 검색의 최적화 가능). 그러나 그것은 토치 제작을위한 몇 가지 옵션을 남겨두고 기본적으로 어디서나 사용할 수 있습니다. 먼저 레시피의 크기를 얻으면 횃불에 6 가지 레시피를 추가 할 필요가 없으며 한 단계에서 올바른 크기의 레시피로만 검색을 제한 할 수 있습니다. -기본적으로 포인트 2의 옵션은 크기별로 그룹화되어 있습니다. 레시피를 코너로 이동하거나 중복 레시피를 추가하십시오.
Elva

15

3x3 그리드를 문자열로 인코딩하고 정규식 일치를 사용 하면 특정 그리드 구성이 특정 레시피와 일치하는지 확인하는 것이 간단합니다 . 조회 속도를 높이는 것은 다른 문제이며 결국 이야기하겠습니다. 자세한 내용은 계속 읽으십시오.

1 단계) 그리드를 문자열로 인코딩

각 셀 유형에 문자 ID를 지정하고 다음 순서대로 모든 항목을 나란히 연결하십시오.

123
456 => 123456789
789

보다 구체적인 예로, W는 나무를 나타내고 E는 빈 셀입니다 (빈 문자 ''를 사용할 수 있음).

EEE
WEE => EEEWEEWEE
WEE

2 단계) 정규 표현식 (또는 문자열을 사용하여 레시피 일치) 데이터에 약간의 처리가 포함되어 있습니다.

위의 예에서 계속해서, 우리가 포메이션을 움직여도 문자열에는 여전히 패턴이 있습니다 (양쪽에 E로 채워진 WEEW).

EEW
EEW => EEWEEWEEE
EEE

따라서 스틱을 어디에서 움직여도 여전히 다음 정규식과 일치합니다. /^E*WEEWE*$/

정규식을 사용하면 언급 한 조건부 동작을 수행 할 수도 있습니다. 예를 들어 (레시피 구성) 철 이나 석재로 만든 곡괭이가 같은 결과 를 원할 경우 :

III    SSS
EWE or EWE
EWE    EWE

정규식으로 두 가지를 결합 할 수 있습니다. /^(III)|(SSS)EWEEWE$/

수평 플립도 마찬가지로 쉽게 추가 할 수 있습니다 (연산자를 사용하여).

편집 : 어쨌든 정규 표현식 부분이 꼭 필요한 것은 아닙니다. 단일 식으로 문제를 캡슐화하는 한 가지 방법 일뿐입니다. 그러나 가변 위치 문제의 경우 모든 패딩 공간 (또는이 예제에서 E)의 그리드 문자열을 자르고 String.Contains ()를 수행 할 수 있습니다. 그리고 다중 성분 문제 또는 미러 된 레시피의 경우, 동일한 출력을 가진 모든 (즉, 별도의) 레시피로 모든 것을 처리 할 수 ​​있습니다.

3 단계) 조회 속도 향상

검색을 줄이려면 레시피를 그룹화하고 조회에 도움이되는 일부 데이터 구조를 작성해야합니다. 그리드를 문자열로 취급하면 여기에도 몇 가지 장점이 있습니다 .

  1. 비어 있지 않은 첫 번째 문자와 비어 있지 않은 마지막 문자 사이의 거리로 레시피의 "길이"를 정의 할 수 있습니다. 간단한 Trim().Length()정보 만 제공하면됩니다. 레시피는 길이별로 그룹화하여 사전에 저장할 수 있습니다.

    또는

    "길이"의 다른 정의는 비어 있지 않은 문자 수입니다. 다른 것은 변하지 않습니다. 이 기준에 따라 레시피를 그룹화 할 수도 있습니다.

  2. 포인트 번호 1이 충분하지 않은 경우 레시피는 레시피에 나타나는 첫 번째 재료의 유형별로 그룹화 될 수 있습니다. 이것은 수행하는 것만 큼 간단합니다 Trim().CharAt(0)(그리고 트림을 방지하여 빈 문자열을 생성 함).

예를 들어 레시피는

Dictionary<int, Dictionary<char, List<string>>> _recipes;

그리고 다음과 같이 조회를 수행하십시오.

// A string encode of your current grid configuration 
string grid;

// Get length and first char in our grid
string trim = grid.Trim();
int length = trim.Length();
char firstChar = length==0 ? ' ' : trim[0];

foreach(string recipe in _recipes[length][firstChar])
{
    // Check for a match with the recipe
    if(Regex.Match(grid, recipe))
    {
        // We found a matching recipe, do something with it
    }
}

3
이것은 ... 매우 똑똑합니다.
Raveline

18
문제가 있고 정규식을 사용하여 문제를 해결 하시겠습니까? 이제 두 가지 문제가 있습니다 :)
Kromster

2
@Krom 아마 당신이 농담하고 있다고 생각하지만 그 의견 뒤에 어떤 추론이 있습니까? 정규 표현식을 사용하는 것은 간결하고 (한 줄의 코드로 레시피와 그리드를 일치시키는 것), 데이터 세트 (그리드 내용)에서 일치를 수행하는 유연한 (이 문제의 모든 요구 사항에 맞음) 방법입니다. 자체 일치 알고리즘을 롤링하는 것보다 큰 단점은 없으며 전체 프로세스를 단순화합니다.
David Gouveia

2
@DavidGouveia, 이것은 Regex에 너무 익숙하지 않은 사람들을위한 문구입니다. 정규식으로 문제를 해결하기로 결정했다면 이제 두 가지 문제가 있습니다. (1) 실제 해결하려는 문제 (2) 문제를 해결하기 위해 정규식 패턴 작성
iamserious

2
'이제 두 가지 문제가 있습니다'로 돌아 가기-정규식 프로세서가 유니 코드를 지원하지 않고 특정 조합이 정규식 NFA 컴파일러를 트립하지 않도록 보장하지 않는 한 정규식은 ASCII 인쇄 가능 범위보다 많은 항목이있는 즉시 문제가 발생합니다 쪽으로. 패턴에 맞게 자신 만의 DFA (실제로는 매우 쉽게)를 함께 던질 수 있습니다. 그러나 정규식은 프로토 타이핑하는 동안 갈 수있는 가장 좋은 방법입니다.
Jonathan Dickinson

3

Minecraft가 어떻게 작동하는지 말할 수는 없지만 MCP (법적 사본이 Minecraft가있는 경우)를 보더라도 확실하게 알 수 있습니다.

나는 이것을 다음과 같이 구현할 것이다.

  1. 디스크 기반 사전 / 해시 맵 ( B + Tree / SQL-Light )이 있어야합니다. 값은 레시피 결과의 항목 ID입니다.
  2. 사용자가 뭔가 공예되면 단순히 항목을 가지고있는 첫 번째 행 / 열 찾을 독립적를 ; 그것들을 오프셋으로 결합하십시오.
  3. 항목이있는 마지막 행 / 열을 다시 독립적으로 찾으십시오.
  4. 항목에서 너비와 높이를 계산하여 키에 추가하십시오.
  5. 방금 계산 한 경계 상자 범위를 반복하고 각 항목의 ID를 추가하십시오 (일반적인 사용 for (y) { for (x) }).
  6. 데이터베이스에서 키를 찾으십시오.

예를 들어 두 가지 성분이 있다고 가정하겠습니다. X 및 Y 및 공백은 *입니다. 다음 레시피를 사용하십시오.

**X
**Y
**Y

먼저 경계 상자를 해결하여을 산출 (2,0)-(2,2)합니다. 따라서 키는 다음과 같습니다 [1][3](1 너비, 3 높이). 다음으로 우리는 경계 상자 내의 각 항목을 반복하고 ID를 추가하여 키가됩니다 [1][3][X][Y][Y]-그런 다음 사전 / DB에서 이것을 조회하면 해당 레시피의 결과를 얻습니다.

2 단계의 독립성을보다 명확하게 설명하려면 다음 레시피를 고려하십시오.

*XX
XXX
XX*

상단 / 왼쪽은 분명히 0,0입니다. 그러나 일반적으로 나타나는 첫 번째 항목은 0,1 또는 1,0입니다 (루프에 따라 다름). 그러나 비어 있지 않은 첫 번째 열과 비어 있지 않은 첫 번째 행을 찾아 해당 좌표를 결합하면 0,0이됩니다. 동일한 주체가 경계 상자의 맨 아래 / 오른쪽에 적용됩니다.


Bukkit 저장소에는 Minecraft 코드가 없으므로 MCP (및 Minecraft 사본)가 필요합니다.
BlueRaja-Danny Pflughoeft

이것은 당신의 다른 대답의 역수가 아닙니까?
David Eyk

@DavidEyk (이것은 나의 첫 번째 대답이다) 실제로 해시 맵 구조에 더 의존적이다 (큰 테이블과 같은 것이 든 메모리에 있든). 내가 다시 대답 한 이유는 적어도 메모리 내 해시 맵에서 성능 저하로 이어지는 해시 충돌에 대해 걱정했기 때문입니다. 내 다른 대답은 더 많은 항목과 메모리 내 구조에서 더 잘 작동합니다 .SQL / etc에 있으면 더 잘 작동합니다.
Jonathan Dickinson

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.