Điểm một trò chơi của Kingdom Builder


16

Tôi muốn thử một hình thức mới của mã golf ở đây. Tương tự như phần thưởng, không phải tất cả các phần của thử thách phải được hoàn thành, nhưng mỗi câu trả lời phải thực hiện một tập hợp con có kích thước nhất định (và có một cốt lõi mà mọi câu trả lời phải thực hiện). Vì vậy, bên cạnh việc chơi golf, thử thách này còn liên quan đến việc lựa chọn một bộ tính năng kết hợp tốt với nhau.

Những quy định

Kingdom Builder là một trò chơi cờ, được chơi trên lưới hex (đỉnh nhọn). Bảng được tạo thành từ bốn góc phần tư (ngẫu nhiên), mỗi ô có các ô lục giác 10 x10 (vì vậy một bảng đầy đủ sẽ là 20x20). Đối với mục đích của thử thách này, mỗi ô hex chứa nước ( W), núi ( M) một thị trấn ( T), lâu đài ( C) hoặc trống ( .). Vì vậy, một góc phần tư có thể trông giống như

. . W . . . . . . .
 . M W W . . . . . .
. M . . W . . . T .
 M M . W . . . . . .
. . M . W W . . . .
 . . . . . W W W W W
. T . . . . . . . .
 . . W . . C . . . .
. . W W . . . . M . 
 . . . . . . . M M .

Hàng thứ hai sẽ luôn được bù sang phải từ hàng đầu tiên. Người chơi 1để 4có thể đặt tối đa 40 khu định cư từng vào ô trống (sau đây một số nguyên tắc mà chúng tôi sẽ bỏ qua cho thách thức này). Một bảng có thể vào cuối của trò chơi là như sau:

3 3 W . . . 4 . 4 . . 2 W . 4 . . 4 . 4
 3 M W W . 1 1 . . 4 2 W . 3 C 4 4 . . 4
3 M 2 2 W 1 1 1 T 3 2 W 4 3 . 1 4 . 4 .
 M M . W 2 2 . . . 2 2 W 3 . 1 1 1 . . .
. 4 M . W W 2 2 2 2 W W 3 . 1 4 . T . .
 . . . . . W W W W W . 3 C 1 . . 2 2 2 2
. T 1 1 1 1 . . 2 . . 4 . . . 2 2 M M M
 4 . W 4 . C 4 4 . . . . . . 2 M M M M M
. 4 W W . . . 4 M . . W . W . 2 2 2 M M
 . . . . . . . M M . . W W . . . . 2 M .
. . . 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 . 1
 M 3 3 . . . . . . . . 4 . T 2 . 2 4 1 .
M M . C . 4 . 4 . . . . . 1 2 4 2 1 1 .
 M . . 1 . 4 . . . . M M 1 2 . . 2 1 . .
. . . W 1 1 4 1 1 . . . 1 2 . . 2 W W W
 . . 1 1 W 1 T . 1 1 1 1 T . . 2 W . 4 .
. 1 1 W . 3 3 . . . . . . . . 2 W 4 C 3
 C 1 3 3 3 . 3 . 4 . 4 . 4 . . 2 W 1 1 M
4 3 3 4 . M 4 3 . . . . . . . 2 W . . .
 . . . 4 . M M 3 . . 4 4 . 4 . 2 W W . .

Chúng tôi sẽ gắn nhãn các góc phần tư như

1 2
3 4

Nhiệm vụ của bạn sẽ là ghi bàn như vậy. Có một điểm cốt lõi luôn được sử dụng và 8 điểm tùy chọn, 3 trong số đó được chọn cho mỗi trò chơi. Trong phần tiếp theo, tôi sẽ mô tả tất cả 9 điểm và sử dụng các thiết lập ở trên là một ví dụ cho bao nhiêu điểm mỗi người chơi sẽ nhận được.

Có 10 điểm trong trò chơi thực tế, nhưng tôi sẽ bỏ qua hai điểm vì không ai muốn đánh gôn chúng.

Điểm số cốt lõi. Một người chơi được 3 điểm cho mỗi Castle họ có một giải quyết bên cạnh. Điểm ví dụ: 18, 0, 15, 12.

Các điểm số tùy chọn.

  1. Một người chơi được 1 điểm cho mỗi hàng ngang mà họ có ít nhất một lần giải quyết.

    Điểm ví dụ: 14, 20, 12, 16.

  2. Đối với mỗi người chơi, hãy tìm hàng ngang mà họ hầu hết các khu định cư của họ (chọn bất kỳ trong trường hợp hòa). Một người chơi được 2 điểm cho mỗi lần giải quyết trên hàng đó.

    Điểm ví dụ: 14 (hàng 16), 8 (hàng 4, 5 hoặc 6), 28 (hàng 11), 10 (hàng 1).

  3. Một người chơi được 1 điểm cho mỗi khu vực được xây dựng bên cạnh Water.

    Điểm ví dụ: 13, 21, 10, 5.

  4. Một người chơi được 1 điểm cho mỗi lần định cư bên cạnh một Mcú.

    Điểm ví dụ: 4, 12, 8, 4.

  5. Đếm các khu định cư của mỗi người chơi trong mỗi góc phần tư. Trên mỗi góc phần tư, những người chơi có số lượng khu định cư lớn nhất nhận được 12 điểm mỗi người, những người chơi có số lượng khu định cư lớn thứ hai nhận được 6 điểm .

    Điểm ví dụ: 18 (6 + 0 + 6 + 6), 36 (12 + 12 + 0 + 12), 12 (0 + 0 + 12 + 0), 18 (12 + 6 + 0 + 0).

  6. Đối với mỗi người chơi xác định góc phần tư trong đó họ có số lượng khu định cư ít nhất. Một người chơi được 3 điểm cho mỗi lần giải quyết trong góc phần tư đó.

    Điểm ví dụ: 18 (Quadrant 2), 0 (Quadrant 3), 15 (Quadrant 1 hoặc 2), 27 (Quadrant 3).

  7. Một người chơi được 1 điểm cho mỗi nhóm khu định cư được kết nối.

    Điểm ví dụ: 7, 5, 6, 29.

  8. Một người chơi được 1 điểm cho mỗi 2 khu định cư trong nhóm các khu định cư được kết nối lớn nhất của người chơi.

    Điểm ví dụ: 4, 10, 8, 2.

Các thách thức

Như trong trò chơi, bạn sẽ chọn 3 điểm số tùy chọn và ghi một bảng cho trước dựa trên điểm số chính và ba điểm số đó. Mã của bạn sẽ tạo ra một danh sách 4 điểm. Có một hạn chế đối với lựa chọn: Tôi đã nhóm điểm thành 3 nhóm và bạn phải thực hiện một trong mỗi nhóm:

  • Thực hiện một trong 1 và 2 .
  • Thực hiện một trong 3, 4, 5 và 6 .
  • Thực hiện một trong 7 và 8 .

Bạn có thể viết chương trình hoặc hàm, lấy đầu vào qua STDIN, đối số dòng lệnh, dấu nhắc hoặc tham số hàm. Bạn có thể trả lại kết quả hoặc in nó cho STDOUT.

Bạn có thể chọn bất kỳ định dạng danh sách / chuỗi 1D hoặc 2D thuận tiện cho đầu vào. Bạn không thể sử dụng biểu đồ với thông tin liên tục đầy đủ. Dưới đây là một số đọc tốt trên lưới hex nếu bạn cần nguồn cảm hứng.

Đầu ra của bạn cũng có thể ở bất kỳ định dạng chuỗi hoặc danh sách thuận tiện, rõ ràng.

Đây là mã golf, vì vậy câu trả lời ngắn nhất (tính bằng byte) sẽ thắng.

Giả định thêm

Bạn có thể cho rằng ...

  • ... Mỗi người chơi có ít nhất 1 khu định cư và không có hơn 40 khu định cư của mỗi người chơi.
  • ... Mỗi góc phần tư chứa một thị trấn và hai lâu đài, hoặc hai thị trấn và một lâu đài.
  • ... Các thị trấn và lâu đài cách nhau đủ xa, đến nỗi không có khu định cư nào có thể liền kề với hai trong số chúng.

Các trường hợp thử nghiệm

Vẫn sử dụng bảng trên, đây là các điểm riêng cho tất cả các lựa chọn có thể có của cơ chế tính điểm:

Chosen Scores      Total Player Scores
1 3 7              52 46 43 62
1 3 8              49 51 45 35
1 4 7              43 37 41 61
1 4 8              40 42 43 34
1 5 7              57 61 45 75
1 5 8              54 66 47 48
1 6 7              57 25 48 84
1 6 8              54 30 50 57
2 3 7              52 34 59 56
2 3 8              49 39 61 29
2 4 7              43 25 57 55
2 4 8              40 30 59 28
2 5 7              57 49 61 69
2 5 8              54 54 63 42
2 6 7              57 13 64 78
2 6 8              54 18 66 51

Có một bảng trong đó một người chơi luôn luôn thắng, bất kể sự kết hợp?
ThreeFx

@ThreeFx Vì giới hạn dưới của số lượng khu định cư trên mỗi người chơi là 1, nên việc thiết lập khá đơn giản. ;) Nhưng với cùng một số khu định cư cho mỗi người chơi, tôi thực sự không biết.
Martin Ender

Câu trả lời:


5

Python 2, 367 byte

T=range(20)
N=lambda r,c:{(a,b)for a,b in{(r+x/3-1,c+x%3-1+(x/3!=1)*r%2)for x in[0,1,3,5,6,7]}if-1<b<20>a>-1}
def S(B):
 def F(r,c):j=J[r][c]!=i;J[r][c]*=j;j or map(F,*zip(*N(r,c)));return j
 J=map(list,B);X=lambda r,c,x,y:x+y in{B[r][c]+B[a][b]for a,b in N(r,c)};return[sum((i in B[r])+20*(3*X(r,c,"C",i)-~X(r,c,i,"W")-F(r,c))for r in T for c in T)/20for i in"1234"]

Chương trình sử dụng điểm 1, 3, 7. Đầu vào là danh sách các danh sách ký tự đại diện cho mỗi ô. Để kiểm tra bảng ví dụ một cách dễ dàng, chúng ta có thể làm:

board = """
3 3 W . . . 4 . 4 . . 2 W . 4 . . 4 . 4
 3 M W W . 1 1 . . 4 2 W . 3 C 4 4 . . 4
3 M 2 2 W 1 1 1 T 3 2 W 4 3 . 1 4 . 4 .
 M M . W 2 2 . . . 2 2 W 3 . 1 1 1 . . .
. 4 M . W W 2 2 2 2 W W 3 . 1 4 . T . .
 . . . . . W W W W W . 3 C 1 . . 2 2 2 2
. T 1 1 1 1 . . 2 . . 4 . . . 2 2 M M M
 4 . W 4 . C 4 4 . . . . . . 2 M M M M M
. 4 W W . . . 4 M . . W . W . 2 2 2 M M
 . . . . . . . M M . . W W . . . . 2 M .
. . . 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 . 1
 M 3 3 . . . . . . . . 4 . T 2 . 2 4 1 .
M M . C . 4 . 4 . . . . . 1 2 4 2 1 1 .
 M . . 1 . 4 . . . . M M 1 2 . . 2 1 . .
. . . W 1 1 4 1 1 . . . 1 2 . . 2 W W W
 . . 1 1 W 1 T . 1 1 1 1 T . . 2 W . 4 .
. 1 1 W . 3 3 . . . . . . . . 2 W 4 C 3
 C 1 3 3 3 . 3 . 4 . 4 . 4 . . 2 W 1 1 M
4 3 3 4 . M 4 3 . . . . . . . 2 W . . .
 . . . 4 . M M 3 . . 4 4 . 4 . 2 W W . .
"""

board = [row.split() for row in board.strip().split("\n")]
print S(board)

# [52, 46, 43, 62]

Xử lý lưới hex

Vì chúng tôi ở trên lưới hex, chúng tôi phải đối phó với hàng xóm một chút khác biệt. Nếu chúng ta sử dụng lưới 2D truyền thống làm đại diện, thì (1, 1)chúng ta có:

. N N . .       . N N . .                (0, 1), (0, 2)            (-1, 0), (-1, 1)
 N X N . .  ->  N X N . .  -> Neighbours (1, 0), (1, 2) -> Offsets (0, -1), (0, 1)
. N N . .       . N N . .                (2, 1), (2, 2)            (1, 0), (1, 1)

Khi kiểm tra kỹ hơn, chúng tôi nhận thấy rằng các khoản bù đắp phụ thuộc vào tính chẵn lẻ của hàng bạn đang sử dụng. Ví dụ trên dành cho các hàng lẻ, nhưng trên các hàng chẵn thì các giá trị bù

(-1, -1), (-1, 0), (0, -1), (0, 1), (1, -1), (1, 0)

Điều duy nhất đã thay đổi là các cặp thứ 1, 2, 5 và 6 đã có tọa độ thứ hai giảm đi 1.

Hàm lambda Ncó một cặp tọa độ (row, col)và trả về tất cả các lân cận của ô trong lưới. Sự hiểu biết bên trong tạo ra các độ lệch trên bằng cách trích xuất chúng từ mã hóa cơ sở 3 đơn giản, tăng tọa độ thứ hai nếu hàng lẻ và thêm các độ lệch vào ô được đề cập để cung cấp cho hàng xóm. Sự hiểu biết bên ngoài sau đó lọc, chỉ để lại những hàng xóm nằm trong giới hạn của lưới điện.

Bị đánh cắp

def neighbours(row, col):
    neighbour_set = set()

    for dr, dc in {(-1,-1), (-1,0), (0,-1), (0,1), (1,-1), (1,0)}:
        neighbour_set.add((row + dr, col + dc + (1 if dr != 0 and row%2 == 1 else 0)))

    return {(r,c) for r,c in neighbour_set if 20>r>-1 and 20>c>-1}

def solve(board):
    def flood_fill(char, row, col):
        # Logic negated in golfed code to save a few bytes
        is_char = (dummy[row][col] == char)
        dummy[row][col] = "" if is_char else dummy[row][col]

        if is_char:
            for neighbour in neighbours(row, col):
                flood_fill(char, *neighbour)

        return is_char

    def neighbour_check(row, col, char1, char2):
        return board[row][col] == char1 and char2 in {board[r][c] for r,c in neighbours(row, col)}

    dummy = [row[:] for row in board] # Need to deep copy for the flood fill
    scores = [0]*4

    for i,char in enumerate("1234"):
        for row in range(20):
            for col in range(20):
                scores[i] += (char in board[row])                        # Score 1
                scores[i] += 20 * 3*neighbour_check(row, col, "C", char) # Core score
                scores[i] += 20 * neighbour_check(row, col, char, "W")   # Score 3
                scores[i] += 20 * flood_fill(char, row, col)             # Score 7

        # Overcounted everything 20 times, divide out
        scores[i] /= 20

    return scores

Không thể def Flà một chức năng riêng biệt hơn là một chức năng nội bộ? Không thể kđược gỡ bỏ từ def F:?
Justin

@Quincunx Flà chức năng lấp đầy lũ lụt và cần quyền truy cập J, vì vậy, nó ở bên trong để tiết kiệm chuyển qua Jlàm tham số (Tôi sẽ thử nghiệm một chút để xem liệu tôi có thể khắc phục được việc sao chép sâu không). kMặc dù vậy, bạn đã đúng , cảm ơn :) (mã mới có vẻ hơi vui nhộn, do dựa vào việc đoản mạch)
Sp3000

2

Lập trình bộ trả lời, 629 byte

d(X,Y):-b(X,Y,_).p(1;2;3;4).n(X,Y,(((X-2;X+2),Y);((X-1;X+1),(Y-1;Y+1)))):-d(X,Y).n(X,Y,I,J):-n(X,Y,(I,J));d(I,J).t(X,Y,P):-n(X,Y,I,J);b(I,J,P).s(c,P,S*3):-S={t(X,Y,P):b(X,Y,"C")};p(P).s(1,P,S*1):-S=#count{r(Y):b(_,Y,P)};p(P).s(3,P,S):-S={b(X,Y,P):t(X,Y,"W")};p(P).o(X,Y,Y+X*100):-d(X,Y).h(P,X,Y,I,J):-o(X,Y,O);o(I,J,Q);O<Q;n(X,Y,I,J);b(X,Y,P);b(I,J,P);p(P).h(P,X,Y,I,J):-o(X,Y,O);o(I,J,Q);O<Q;h(P,X,Y,K,L);n(K,L,I,J);b(I,J,P);p(P).c(P,X,Y):-h(P,X,Y,_,_);not h(P,_,_,X,Y).c(P,X,Y):-{h(P,X,Y,_,_);h(P,_,_,X,Y)}0;b(X,Y,P);p(P).s(7,P,S):-S=#count{c(P,X,Y):c(P,X,Y)};p(P).s(t,P,C+S+T+U):-s(c,P,C);s(1,P,S);s(3,P,T);s(7,P,U).#shows/3.

ASP thuộc họ ngôn ngữ lập trình logic, ở đây được tái sinh bởi khung Potassco , đặc biệt là Clingo (Gringo + solver clerer). Do giới hạn mô hình, nó không thể lấy bảng được cung cấp trực tiếp làm đầu ra, do đó, việc xử lý trước dữ liệu là cần thiết (ở đây được thực hiện bằng python). Quá trình tiền xử lý này không được tính vào tổng số byte.

Đây là môn đánh gôn đầu tiên của tôi, và mục tiêu là thể hiện một ngôn ngữ mà tôi yêu thích mà tôi chưa từng thấy trước đây trong môn golf, hơn là thực sự chiến thắng trò chơi. Hơn nữa, tôi ở xa một chuyên gia về ASP, vì vậy nhiều tối ưu hóa mã chắc chắn có thể được thực hiện để có kết quả với ít byte hơn.

đại diện kiến ​​thức

Có mã python chuyển đổi bảng trong nguyên tử:

def asp_str(v):
    return ('"' + str(v) + '"') if v not in '1234' else str(v)

with open('board.txt') as fd, open('board.lp', 'w') as fo:
        [fo.write('b('+ str(x) +','+ str(y) +','+ asp_str(v) +').\n')
         for y, line in enumerate(fd)
         for x, v in enumerate(line) if v not in ' .\n'
        ]

Ví dụ: các nguyên tử b (cho __b__oard) được cung cấp cho dòng đầu tiên của bảng ví dụ là các nội dung sau:

b(0,0,3).
b(2,0,3).
b(4,0,"W").
b(12,0,4).
b(16,0,4).
b(22,0,2).
b(24,0,"W").
b(28,0,4).
b(34,0,4).
b(38,0,4).

Trong đó b (0,0,3) là một nguyên tử mô tả rằng người chơi 3 có độ lún tại tọa độ (0; 0).

Giải quyết ASP

Có mã ASP, với nhiều điểm tùy chọn được triển khai:

% input : b(X,Y,V) with X,Y the coordinates of the V value

domain(X,Y):- b(X,Y,_).
player("1";"2";"3";"4").

% neighbors of X,Y
neighbors(X,Y,((X-2,Y);(X+2,Y);((X-1;X+1),(Y-1;Y+1)))) :- domain(X,Y).
neighbors(X,Y,I,J):- neighbors(X,Y,(I,J)) ; domain(I,J).

% Player is next to X,Y iff has a settlement next to.
next(X,Y,P):- neighbors(X,Y,I,J) ; b(I,J,P).


% SCORES

% Core score : 3 point for each Castle "C" with at least one settlement next to.
score(core,P,S*3):- S={next(X,Y,P): b(X,Y,"C")} ; player(P).

% opt1: 1 point per settled row
score(opt1,P,S*1):- S=#count{row(Y): b(_,Y,P)} ; player(P).

% opt2: 2 point per settlement on the most self-populated row
% first, defines how many settlements have a player on each row
rowcount(P,Y,H):- H=#count{col(X): b(X,Y,P)} ; domain(_,Y) ; player(P).
score(opt2,P,S*2):- S=#max{T: rowcount(P,Y,T)} ; player(P).

% opt3: 1 point for each settlements next to a Water "W".
score(opt3,P,S):- S={b(X,Y,P): next(X,Y,"W")} ; player(P).

% opt4: 1 point for each settlements next to a Mountain "M".
score(opt4,P,S):- S={b(X,Y,P): next(X,Y,"M")} ; player(P).

% opt5:
%later…

% opt6:
%later…

% opt7: 1 point for each connected component of settlement
% first we need each coord X,Y to be orderable.
% then is defined path/5, that is true iff exists a connected component of settlement of player P
%   that links X,Y to I,J
% then is defined the connected component atom that give the smaller coords in each connected component
% then computing the score.
order(X,Y,Y+X*100):- domain(X,Y).
path(P,X,Y,I,J):- order(X,Y,O1) ; order(I,J,O2) ; O1<O2 ; % order
                  neighbors(X,Y,I,J) ; b(X,Y,P) ; b(I,J,P) ; player(P). % path iff next to
path(P,X,Y,I,J):- order(X,Y,O1) ; order(I,J,O2) ; O1<O2 ; % order
                  path(P,X,Y,K,L) ; neighbors(K,L,I,J) ; % path if path to next to
                  b(I,J,P) ; player(P).
concomp(P,X,Y):- path(P,X,Y,_,_) ; not path(P,_,_,X,Y). % at least two settlements in the connected component
concomp(P,X,Y):- 0 { path(P,X,Y,_,_) ; path(P,_,_,X,Y) } 0 ; board(X,Y,P) ; player(P). % concomp of only one settlements
score(opt7,P,S):- S=#count{concomp(P,X,Y): concomp(P,X,Y)} ; player(P).

% opt8: 0.5 point for each settlement in the bigger connected component
%later…


% total score:
score(total,P,C+S1+S2+S3):- score(core,P,C) ; score(opt1,P,S1) ; score(opt3,P,S2) ; score(opt7,P,S3).

#show. # show nothing but the others show statements
#show total_score(P,S): score(total,P,S).
%#show score/3. % scores details

Chương trình này có thể được khởi chạy với lệnh:

clingo board.lp golf.lp 

Và sẽ chỉ tìm thấy một giải pháp (đó là bằng chứng cho thấy chỉ có một cách để phân phối điểm):

s(c,1,18) s(c,2,0) s(c,3,15) s(c,4,12) s(1,1,14) s(1,2,20) s(1,3,12) s(1,4,16) s(3,1,13) s(3,2,21) s(3,3,10) s(3,4,5) s(7,1,7) s(7,2,5) s(7,3,6) s(7,4,29) s(t,1,52) s(t,2,46) s(t,3,43) s(t,4,62)

Trong đó s (7,3,6) nói rằng người chơi 3 tăng 6 điểm với số điểm tùy chọn 7 và s (t, 4,62) nói rằng người chơi 4 có tổng cộng 62 điểm (lõi + 1 + 3 + 7).

Dễ dàng phân tích để có một bảng ưa thích!

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.