Làm cách nào tôi có thể triển khai thứ gì đó như lưới chế tạo của Minecraft?


55

Các hệ thống crafting trong Minecraft sử dụng một 2x2 hoặc 3x3 lưới. Bạn đặt các thành phần trên lưới, và nếu bạn đặt đúng thành phần theo đúng mẫu, nó sẽ kích hoạt công thức.

Một số điểm thú vị về thiết kế:

  • Một số công thức nấu ăn có thể trao đổi một số thành phần nhất định cho người khác. Ví dụ, một lựa chọn sử dụng gậy cho thân cây và có thể sử dụng ván gỗ, đá cuội, thỏi sắt, thỏi vàng hoặc đá quý kim cương cho đầu.
  • Vị trí tương đối trong mẫu là những gì quan trọng, không phải vị trí tuyệt đối trên lưới. Nghĩa là, bạn có thể chế tạo một ngọn đuốc bằng cách đặt cây gậy và than (hoặc than) theo đúng mẫu ở bất kỳ vị trí nào trong sáu vị trí trên lưới 3x3.
  • Các mẫu có thể được lật theo chiều ngang.

Tôi có thể đang xem xét lại nó, nhưng đây có vẻ như là một vấn đề tìm kiếm / giảm tập hợp thú vị. Vì vậy, làm thế nào (hoặc có thể) công việc này, nói theo thuật toán?


6
Câu hỏi tuyệt vời! Tôi rất quan tâm! Tôi đã có một số ý tưởng nhưng tôi sẽ chia sẻ chúng ngay khi về đến nhà.
jcora

Tôi thực sự đang cố gắng viết một bản sao Minecraft "càng gần càng tốt". Tôi đã viết tất cả các công cụ quản lý kho và công thức đã có và tôi phải nói rằng nó đang hoạt động rất gọn gàng. Tôi hài lòng về mã sạch. Tuy nhiên, thật không dễ để viết nó. Tôi đã viết khoảng 10 lớp để làm cho tất cả hoạt động độc đáo các công thức nấu ăn, lưới, kho, v.v. Khi tôi tìm thấy một thời gian, tôi sẽ viết một câu trả lời.
Martijn Courteaux

Bạn nên nhìn vào cách minecraft mã hóa công thức chế tạo của họ.
Bradman175

Câu trả lời:


14

Một giải pháp khác là sử dụng một cây hơi phức tạp. Các nút nhánh trong cây của bạn sẽ được tạo bằng cách lặp qua công thức (một lần nữa sử dụng for (y) { for (x) }); đây là cấu trúc cây theo tiêu chuẩn chứng khoán của bạn. Nút cuối cùng của bạn sẽ chứa cấu trúc bổ sung ( Dictionary/ HashMap) ánh xạ kích thước vào công thức nấu ăn.

Về cơ bản những gì bạn đang làm là đây:

Cây công thức

Các nút màu đen là các nhánh của bạn cho biết loại vật phẩm - các nút màu đỏ là lá của bạn (dấu chấm dứt) cho phép bạn phân biệt kích thước / hướng.

Để tìm kiếm trên cây này, trước tiên bạn sẽ tìm thấy hộp giới hạn (như được nêu trong câu trả lời đầu tiên của tôi ) và sau đó lặp lại qua các nút theo cùng thứ tự đi ngang qua cây khi bạn đi. Cuối cùng, bạn chỉ cần tìm kích thước trong Dictionaryhoặc HashMapbạn sẽ nhận được kết quả của công thức.

Chỉ vì những cú đá tôi đã đi và thực hiện điều này - có lẽ sẽ làm rõ câu trả lời của tôi. Ngoài ra : Tôi nhận ra đây là một câu trả lời khác - và đúng như vậy: đó là một giải pháp khác.


Rất đơn giản. Tôi thích nó.
David Eyk

Tôi sẽ chấp nhận điều này, bởi vì tôi thích cây cối, băm và sơ đồ cắt vào trung tâm của vấn đề. Nhìn vào sơ đồ, và tôi hiểu thuật toán của bạn gần như ngay lập tức.
David Eyk

23

Bạn phải nhớ rằng Minecraft chỉ sử dụng một bộ rất nhỏ các công thức có thể, vì vậy không cần bất cứ thứ gì thông minh.

Điều đó nói rằng những gì tôi sẽ làm là tìm lưới nhỏ nhất phù hợp (nghĩa là bỏ qua các hàng và cột trống để tìm hiểu xem đó là 2x2 hay 3x3 hay 2x3 (cửa)). Sau đó lặp qua danh sách các công thức nấu ăn với kích thước đó, chỉ cần kiểm tra xem loại vật phẩm có giống nhau hay không (nghĩa là, 9 so sánh số nguyên tệ nhất trong minecraft vì nó sử dụng id loại số nguyên cho vật phẩm và khối) và dừng lại khi bạn tìm thấy kết quả khớp.

Cách này cũng làm cho vị trí tương đối của các vật phẩm không liên quan (bạn có thể đặt một ngọn đuốc ở bất cứ đâu trên lưới chế tạo và nó sẽ hoạt động vì nó xem nó là hộp 1x2, không phải hộp 3x3 mà hầu như trống rỗng).

Nếu bạn có một lượng lớn công thức để thực hiện tìm kiếm tuyến tính thông qua các kết quả khớp có thể mất nhiều thời gian thì có thể sắp xếp danh sách và thực hiện tìm kiếm nhị phân (O (log (N)) so với O (N)). Điều này sẽ gây ra một số công việc bổ sung trong việc xây dựng danh sách, nhưng điều này có thể được thực hiện khi khởi động một lần và giữ trong bộ nhớ sau đó.

Ngoài ra, một điều cuối cùng, để cho phép lật công thức theo chiều ngang đơn giản nhất là chỉ cần thêm phiên bản được nhân đôi vào danh sách.

Nếu bạn muốn làm điều đó mà không cần thêm công thức thứ hai, bạn có thể kiểm tra xem công thức nhập liệu có một mục trong [0,0] với id cao hơn trong [0,2] (hoặc [0,1] cho 2x2 không, không cần kiểm tra cho 1x2 và nếu phản chiếu nó, nếu không tiếp tục kiểm tra hàng tiếp theo cho đến khi bạn kết thúc. Sử dụng điều này bạn cũng phải đảm bảo các công thức nấu ăn được thêm vào trong vòng xoay chính xác.


1
Tôi tự hỏi nếu một số lượng nhỏ các công thức nấu ăn trong Minecraft có thể không cho vay để tìm kiếm tuyến tính.
David Eyk

1
Nó làm, và về cơ bản là những gì tôi đã viết ở trên (với khả năng tối ưu hóa tìm kiếm nhị phân). Tuy nhiên, điều đó để lại một vài lựa chọn cho việc chế tạo đuốc mà bạn có thể lắp ở bất cứ đâu. Trước tiên, để có được kích thước của công thức, bạn không cần thêm 6 công thức cho đèn pin và bạn giới hạn tìm kiếm của mình chỉ ở những kích thước chính xác trong một bước. - Vì vậy, về cơ bản các tùy chọn cho điểm 2 của bạn được nhóm theo kích thước, di chuyển công thức sang một góc hoặc thêm nhiều công thức trùng lặp.
Elva

15

Việc xem liệu một cấu hình lưới nhất định phù hợp với một công thức nhất định có đơn giản không nếu bạn mã hóa lưới 3x3 dưới dạng chuỗi và sử dụng kết hợp biểu thức chính quy . Tăng tốc tìm kiếm là một vấn đề khác, cuối cùng tôi sẽ nói về nó. Đọc tiếp để biết thêm thông tin.

Bước 1) Mã hóa lưới dưới dạng Chuỗi

Chỉ cần cung cấp một id char cho từng loại ô và ghép mọi thứ cạnh nhau theo thứ tự này:

123
456 => 123456789
789

Và như một ví dụ cụ thể hơn, hãy xem xét công thức thanh, trong đó W là viết tắt của gỗ và E là một ô trống (bạn có thể chỉ cần sử dụng một char trống rỗng ''):

EEE
WEE => EEEWEEWEE
WEE

Bước 2) Kết hợp công thức bằng cách sử dụng biểu thức chính quy (hoặc String.Contains với một chút xử lý trên dữ liệu)

Tiếp tục từ ví dụ trên, ngay cả khi chúng ta di chuyển đội hình xung quanh, vẫn có một mẫu trong chuỗi (WEEW được đệm bởi E ở cả hai bên):

EEW
EEW => EEWEEWEEE
EEE

Vì vậy, bất kể bạn di chuyển cây gậy xung quanh ở đâu, nó vẫn sẽ khớp với biểu thức thông thường sau: /^E*WEEWE*$/

Biểu thức thông thường cũng cho phép bạn thực hiện hành vi có điều kiện mà bạn đề cập. Ví dụ: (đã tạo thành công thức), nếu bạn muốn một cái cuốc được làm bằng sắt hoặc đá để cho kết quả tương tự, tức là:

III    SSS
EWE or EWE
EWE    EWE

Bạn có thể kết hợp cả hai thành biểu thức chính quy: /^(III)|(SSS)EWEEWE$/

Flips ngang cũng có thể được thêm dễ dàng (sử dụng toán tử | quá).

Chỉnh sửa: Dù sao, phần regex không thực sự cần thiết. Đây chỉ là một cách để gói gọn vấn đề trong một biểu thức duy nhất Nhưng đối với vấn đề vị trí biến, bạn cũng có thể cắt chuỗi lưới của bất kỳ không gian đệm nào (hoặc E trong ví dụ này) và thực hiện String.Contains (). Và đối với vấn đề nhiều thành phần hoặc các công thức được nhân đôi, bạn có thể xử lý tất cả chúng dưới dạng nhiều công thức (nghĩa là riêng biệt) với cùng một đầu ra.

Bước 3) Tăng tốc Tra cứu

Đối với việc giảm tìm kiếm, bạn sẽ cần tạo một số cấu trúc dữ liệu để nhóm các công thức nấu ăn cùng nhau và giúp tìm kiếm. Xử lý lưới như chuỗi cũng có một số lợi thế ở đây :

  1. Bạn có thể định nghĩa "độ dài" của công thức là khoảng cách giữa ký tự không trống đầu tiên và ký tự không trống cuối cùng. Một đơn giản Trim().Length()sẽ cung cấp cho bạn thông tin này. Bí quyết có thể được nhóm theo chiều dài và được lưu trữ trong một từ điển.

    hoặc là

    Một định nghĩa khác về "độ dài" có thể là số lượng ký tự không trống. Không có gì khác thay đổi. Bạn có thể nhóm công thức nấu ăn theo tiêu chí này quá.

  2. Nếu điểm số 1 là không đủ, công thức nấu ăn cũng có thể được nhóm lại theo loại thành phần đầu tiên xuất hiện trong công thức. Điều này sẽ đơn giản như làm Trim().CharAt(0)(và bảo vệ chống lại Trim dẫn đến một chuỗi rỗng).

Vì vậy, ví dụ bạn sẽ lưu trữ các công thức nấu ăn trong:

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

Và thực hiện tra cứu như một cái gì đó như:

// 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
Đây là ... rất rất thông minh.
Raveline

18
Bạn có một vấn đề và bạn muốn giải quyết nó bằng cách sử dụng các biểu thức thông thường? Bây giờ bạn có 2 vấn đề :)
Kromster nói hỗ trợ Monica

2
@ Từ đây tôi có lẽ bạn chỉ nói đùa, nhưng có lý do nào đằng sau nhận xét đó không? Sử dụng biểu thức chính quy chỉ là một cách súc tích (lưới khớp với công thức với một dòng mã) và cách linh hoạt (phù hợp với tất cả các yêu cầu của vấn đề này) để thực hiện khớp trên một tập hợp dữ liệu (nội dung lưới). Tôi thấy không có nhược điểm đáng kể nào so với việc cuộn thuật toán phù hợp của riêng bạn và nó đơn giản hóa toàn bộ quá trình.
David Gouveia

2
@DavidGouveia, đó chỉ là một cụm từ dành cho những người không quá thoải mái với Regex. Nếu họ quyết định để giải quyết một vấn đề với regex, bây giờ họ có hai vấn đề: (1) các vấn đề thực tế mà họ muốn để giải quyết (2) bằng văn bản cho mẫu biểu thức chính để giải quyết vấn đề đó
iamserious

2
Quay trở lại 'bây giờ bạn có hai vấn đề' - Regex sẽ gặp sự cố ngay khi bạn có nhiều mục hơn phạm vi có thể in ASCII, trừ khi bộ xử lý regex của bạn hỗ trợ unicode và bạn có thể đảm bảo rằng các kết hợp nhất định sẽ không gặp trình biên dịch NFA regex lên. Bạn có thể kết hợp DFA của riêng bạn (thực sự khá dễ dàng) để khớp với các mẫu; nhưng regex sẽ là cách tốt nhất để đi trong khi tạo mẫu.
Jonathan Dickinson

3

Tôi không thể cho bạn biết Minecraft hoạt động như thế nào - mặc dù tôi chắc chắn nếu bạn đã xem MCP (nếu bạn có bản sao hợp pháp của Minecraft), bạn có thể tìm hiểu.

Tôi sẽ thực hiện điều này như sau:

  1. Có một số từ điển / hashmap dựa trên đĩa ( B + Tree / SQL-Light ) - giá trị sẽ là ID mục của kết quả công thức.
  2. Khi người dùng đang tạo một cái gì đó chỉ cần tìm hàng / cột đầu tiên với một mục ĐỘC LẬP ; kết hợp chúng thành một phần bù.
  3. Tìm hàng / cột cuối cùng với một mục, một lần nữa, độc lập.
  4. Tính chiều rộng và chiều cao từ các mục và thêm nó vào phím.
  5. Lặp lại phạm vi của khung giới hạn mà bạn vừa tính toán và nối thêm ID của từng mục (sử dụng thông thường của bạn for (y) { for (x) }).
  6. Nhìn chìa khóa trong cơ sở dữ liệu.

Vì vậy, ví dụ cho phép chúng ta có hai thành phần; X và Y và khoảng trống là *. Thực hiện theo công thức sau:

**X
**Y
**Y

Đầu tiên chúng tôi làm việc ra các hộp giới hạn, năng suất (2,0)-(2,2). Do đó, khóa của chúng tôi sẽ trông như thế này [1][3](1 chiều rộng, 3 chiều cao). Tiếp theo, chúng tôi lặp lại từng mục trong hộp giới hạn và nối ID, do đó, khóa sẽ trở thành [1][3][X][Y][Y]- sau đó bạn tra nó trong từ điển / DB của bạn và bạn sẽ nhận được kết quả của công thức đó.

Để giải thích sự độc lập ở bước 2 rõ ràng hơn, hãy xem xét công thức sau:

*XX
XXX
XX*

Trên cùng / bên trái rõ ràng là 0,0 - tuy nhiên mục đầu tiên bạn thường gặp sẽ là 0,1 hoặc 1,0 (tùy thuộc vào vòng lặp của bạn). Tuy nhiên, nếu tìm cột không trống đầu tiên cũng như hàng không trống đầu tiên và kết hợp các tọa độ đó, bạn sẽ nhận được 0,0 - cùng một hiệu trưởng áp dụng cho đáy / bên phải của hộp giới hạn.


Kho lưu trữ Bukkit không có bất kỳ mã Minecraft nào, bạn cần MCP (và bản sao của Minecraft)
BlueRaja - Danny Pflughoeft

Đây không phải là nghịch đảo của câu trả lời khác của bạn?
David Eyk

@DavidEyk (Đây là câu trả lời đầu tiên của tôi) không thực sự, nó phụ thuộc nhiều hơn vào cấu trúc hashmap (cho dù đó là bigtable-like hay in-memory). Lý do tôi trả lời lại là vì tôi lo lắng về các va chạm băm dẫn đến hiệu suất kém - ít nhất là trong một hashmap trong bộ nhớ. Câu trả lời khác của tôi sẽ hoạt động tốt hơn cho nhiều mục hơn và cấu trúc trong bộ nhớ - nơi điều này sẽ hoạt động tốt hơn nếu có trong SQL / etc.
Jonathan Dickinson

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.