Slime: Chiến tranh lãnh thổ


18

Bạn là một thế giới của chất nhờn. Đương nhiên, là chất nhờn, bạn muốn tiết lộ càng nhiều diện tích càng tốt. Nhưng có 3 slime khác muốn làm điều tương tự chính xác. Ai sẽ là chất nhờn vượt trội?

Sự miêu tả

Tất cả các slime sẽ được tập hợp vào một đấu trường. Các thẩm phán (tức là chương trình kiểm soát) sẽ đi qua một danh sách đầy đủ tất cả các kết hợp 4 chất nhờn có thể, đặt chúng trên các góc của bàn và quan sát để xem chất nhờn nào chảy ra trên khu vực nhất.

Slime của bạn có thể thực hiện một trong 3 hành động mỗi lượt: trải ra, nhảy hoặc hợp nhất. Mô tả thêm về ý nghĩa của những điều này sẽ được cung cấp trong phần Đầu ra .

Ban / Đấu trường

Đấu trường sẽ là một bảng vuông (hiện tại là 8x8, nhưng điều này có thể thay đổi trong tương lai). Dưới đây là một ví dụ về một trò chơi đang diễn ra:

11111222
11111444
11.22444
.1222.4.
333.3244
33333.44
333...44
333....4

Slime được thể hiện bằng các số từ 1 đến 4 (người chơi 1 đến 4) và không gian trống được thể hiện bằng dấu chấm ( .). Ban đầu, bảng bắt đầu là tất cả các khoảng trống, ngoại trừ một đơn vị slime của người chơi 1 ở góc trên bên trái, người chơi 2 ở trên cùng bên phải, người chơi 3 ở dưới cùng bên trái và người chơi 4 ở dưới cùng bên phải.

Các tọa độ được biểu diễn bằng chỉ số hàng và cột dựa trên 0, để dễ đọc trong mã. Ví dụ: tọa độ (3, 6) đại diện cho hình vuông thứ 7 trong hàng thứ 4 (trong ví dụ trên, a 4). (Điều này giúp truy cập các ô vuông dễ dàng hơn : board[coords.x][coords.y].) Đây là một minh họa trực quan:

(0, 0) (0, 1) (0, 2)
(1, 0) (1, 1) (1, 2)
(2, 0) (2, 1) (2, 2)

Đầu vào

Đầu vào của chương trình của bạn sẽ là người chơi của bạn (1, 2, 3 hoặc 4), dấu phẩy ( ,), sau đó là nội dung của bảng / đấu trường (với dòng mới được thay thế bằng dấu phẩy). Ví dụ: nếu bạn là người chơi 3 trong kịch bản trên, đầu vào của bạn sẽ là:

3,11111222,11111444,11.22444,.1222.4.,333.3244,33333.44,333...44,333....4

Đầu ra

Chương trình của bạn phải xuất 4 số nguyên. Hai cái đầu tiên là chỉ số hàng và cột tương ứng của chất nhờn bạn muốn di chuyển, và hai cái tiếp theo là chỉ số hàng và cột của nơi bạn muốn di chuyển chúng.

Có ba lựa chọn bạn có trong mỗi lượt: Trải ra, nhảy hoặc hợp nhất.

  • Lan tràn

    Để lây lan, tọa độ đích phải chính xác cách một ô vuông được di chuyển và ô vuông tại tọa độ đích phải là khoảng trống. Khi lây lan, chất nhờn mới được tạo ra tại tọa độ đích và chất nhờn cũ không bị loại bỏ. Sau khi chất nhờn mới được tạo ra, tất cả các chất nhờn của kẻ thù trong 8 ô vuông xung quanh chất nhờn mới này được chuyển đổi thành người chơi đã di chuyển.

    Ví dụ, với bảng trong Hình 1, nếu người chơi 1 xuất ra 0 1 1 2, kết quả sẽ là bảng trong Hình 2.

    1.         2.
      11.22      11.12
      1..22      1.112
      ..22.      ..11.
      .....      .....
    
  • Nhảy

    Để nhảy, tọa độ đích phải chính xác là hai ô vuông cách chất nhờn được di chuyển và hình vuông tại tọa độ đích phải là khoảng trống. Khi jupming, một chất nhờn mới được tạo ra tại tọa độ đích và chất nhờn cũ được loại bỏ. Sau khi chất nhờn mới được tạo ra, tất cả các chất nhờn của kẻ thù trong 8 ô vuông xung quanh chất nhờn mới này được chuyển đổi thành người chơi đã di chuyển.

    Ví dụ, với bảng trong Hình 1, nếu người chơi 1 xuất ra 0 1 2 3, kết quả sẽ là bảng trong Hình 2.

    1.         2.    
      11..2      1...2
      1...2      1...1
      ....2      ...11
      ...22      ...11
    
  • Hợp nhất

    Để hợp nhất, tọa độ đích phải chính xác một ô vuông so với chất nhờn được di chuyển và hình vuông tại tọa độ đích phải là chất nhờn của cùng một người chơi. Khi sáp nhập, chất nhờn cũ được loại bỏ. Sau đó, tất cả các khoảng trống trong 8 ô vuông xung quanh chất nhờn đích được chuyển đổi thành người chơi đã di chuyển (không bao gồm chất nhờn cũ được di chuyển).

    Ví dụ, với bảng trong Hình 1, nếu người chơi 1 xuất ra 0 1 1 2, kết quả sẽ là bảng trong Hình 2.

    1.         2.
      11..2      1.112
      1.1.2      11112
      ....2      .1112
      ..222      ..222
    

Bạn cũng có thể vượt qua, bằng cách xuất ra các tọa độ không hợp lệ (ví dụ 0 0 0 0).

Các quy tắc và ràng buộc

Các quy tắc bổ sung là:

  • Bạn có thể đọc và ghi các tệp trong thư mục của riêng mình để duy trì dữ liệu (các bài nộp sẽ được lưu trữ players/YourBotName/yourBotName.language), nhưng bạn không thể sửa đổi hoặc truy cập bất cứ điều gì khác ngoài nó. Truy cập Internet bị cấm.
  • Việc gửi của bạn có thể không được mã hóa cụ thể để giúp đỡ hoặc làm tổn thương một bài nộp khác. (Bạn có thể có nhiều bài nộp, nhưng chúng không được tương tác cụ thể với nhau theo bất kỳ cách nào.)
  • Việc gửi của bạn phải mất không quá 0,1 giây mỗi lượt. Nếu việc gửi của bạn thỉnh thoảng mất 0,105 giây, điều đó tốt, nhưng nó có thể không nhất quán lâu hơn đáng kể so với giới hạn thời gian này. (Đây chủ yếu là kiểm tra độ tỉnh táo để tránh thử nghiệm mất quá nhiều thời gian.)
  • Nội dung gửi của bạn không được trùng lặp chính xác (nghĩa là sử dụng cùng một logic chính xác) của người khác, ngay cả khi đó là ngôn ngữ khác.
  • Trình của bạn phải là một đệ trình nghiêm túc. Đây là dựa trên ý kiến, nhưng nếu trình của bạn rõ ràng không cố gắng giải quyết thách thức (ví dụ: nếu bạn vượt qua mỗi lượt), nó sẽ bị loại.

Nếu nội dung gửi của bạn vi phạm bất kỳ quy tắc nào trong số những quy tắc này hoặc không tuân theo thông số kỹ thuật, nó sẽ bị loại, bị xóa khỏi playerlist.txtvà trò chơi sẽ khởi động lại từ đầu. Nếu bài đăng của bạn bị loại, tôi sẽ để lại nhận xét về bài đăng của bạn giải thích lý do. Nếu không, trình của bạn sẽ được thêm vào bảng xếp hạng. (Nếu bạn không thấy bài đăng của mình trên bảng xếp hạng, không có bình luận giải thích về bài đăng của bạn và đăng bài đăng của bạn trước thời gian "Cập nhật lần cuối" bên dưới, xin vui lòng cho tôi biết!

Trong mục của bạn, vui lòng bao gồm:

  • Một cái tên.
  • Một lệnh shell để chạy chương trình của bạn (ví dụ java MyBot.java, ruby MyBot.rb, python3 MyBot.py, vv).
    • Lưu ý rằng đầu vào (trình phát và bản đồ của bạn) sẽ được thêm vào đây dưới dạng đối số dòng lệnh.
    • Các chương trình sẽ được kiểm tra trên Ubuntu 14.04, vì vậy hãy đảm bảo mã của bạn có thể được chạy (tự do) trên nó.
  • Số phiên bản, nếu mã của bạn hoạt động khác nhau trên các phiên bản ngôn ngữ khác nhau.
  • Mã bot của bạn.
  • Hướng dẫn cách biên dịch mã, nếu cần.

Mã điều khiển / kiểm tra, ví dụ bot

Mã điều khiển được viết bằng C ++ và có thể được tìm thấy trên Github . Hướng dẫn thêm về cách chạy và kiểm tra mã của bạn có thể được tìm thấy ở đó.

simplebot.rb, một bot rất đơn giản giúp phát tán hoặc nhảy một chất nhờn ngẫu nhiên đến một vị trí ngẫu nhiên mỗi lượt, cũng được đăng trên Github .

Chấm điểm và bảng xếp hạng

Khi tất cả các ô vuông trên bảng được lấp đầy, trò chơi kết thúc và điểm được tính. Điểm cuối cùng của người chơi là số lượng hình vuông chứa chất nhờn của họ vào cuối trò chơi. Nếu 2000 lượt đã trôi qua (500 cho mỗi người chơi) và trò chơi vẫn chưa kết thúc, trò chơi sẽ kết thúc bằng mọi cách và điểm số sẽ được báo cáo như thể trò chơi đã kết thúc.

Vào cuối giải đấu, điểm số của tất cả các trò chơi sẽ được tính trung bình để tính điểm số cuối cùng của mỗi người chơi, sẽ được đăng trên bảng xếp hạng. Không có thời hạn nộp; Tôi sẽ tiếp tục cập nhật bảng xếp hạng theo định kỳ khi có bài nộp mới.

4 bài nộp được yêu cầu cho đến khi bảng xếp hạng thực sự xuất hiện.

+--------------------------+-----------+--------------------+
| Name                     | Avg Score | Last Updated (UTC) |
+--------------------------+-----------+--------------------+
| GreedySlime              | 47.000    | Jul 22 10:27 PM    |
| Jumper                   | 12.000    | Jul 22 10:27 PM    |
| ShallowBlue              | 5.000     | Jul 22 10:27 PM    |
| Lichen                   | 0.000     | Jul 22 10:27 PM    |
+--------------------------+-----------+--------------------+

Cập nhật lần cuối: ngày 22 tháng 7 10:27 PM (UTC).


Hmm, tôi có thể đã bỏ lỡ nó, nhưng bạn đã giải thích sự tương tác giữa những người chơi sẽ như thế nào chưa? Có phải tất cả mọi người di chuyển cùng một lúc? Người chơi 1 trước?
justhalf

1
Có lẽ chỉ có tôi thấy điều này hơi không rõ ràng, nhưng làm thế nào để bạn xác định "hai hình vuông đi", chính xác?
arshajii

Nhắc nhở tôi rất nhiều trò chơi dựa trên đồ uống từ những năm chín mươi. ;-)
Benny

@justhalf Người chơi 1 di chuyển đầu tiên.
Doorknob

1
@arshajii "Hai hình vuông đi" có nghĩa là, chính thức, "tại bất kỳ vị trí nào có mức thay đổi tối đa trong X và thay đổi trong Y bằng 2."
Doorknob

Câu trả lời:


4

Tham lam

Đơn giản chỉ cần thực hiện di chuyển tạo ra mức tăng ròng lớn nhất của các đơn vị chất nhờn.

Lưu ý rằng điều này được viết bằng Python 2.x .

def gen_moves(board, pos):
    """Generate valid moves for a given position.

    Return value is a tuple of the form
       (type, from_x, from_y, to_x, to_y)

    The move 'type' is a single character with:
        - 's' = spread
        - 'j' = jump
        - 'm' = merge
    """

    N = len(board)
    x0, y0 = pos
    player = board[x0][y0]

    for i in -2,-1,0,1,2:
        for j in -2,-1,0,1,2:
            if (i == 0 and j == 0):
                continue

            x1, y1 = x0 + i, y0 + j

            if not ((0 <= x1 < N) and (0 <= y1 < N)):
                continue

            c = board[x1][y1]

            if -1 <= i <= 1 and -1 <= j <= 1:
                if c == '.':
                    yield ('s', x0, y0, x1, y1)
                elif c == player:
                    yield ('m', x0, y0, x1, y1)
            else:
                if c == '.':
                    yield ('j', x0, y0, x1, y1)

def eval_move(board, move, initial_net={'s': 1, 'j': 0, 'm': -1}):
    """Evaluates given move in given context.

    - Assumes move is valid.
    - `move` argument is a tuple of the form
       (type, from_x, from_y, to_x, to_y)
    - The move 'type' is a single character with:
        - 's' = spread
        - 'j' = jump
        - 'm' = merge
    """

    N = len(board)
    move_type = move[0]
    x0, y0, x1, y1 = move[1:]
    player = board[x0][y0]

    net = initial_net[move_type]
    for i in -1,0,1:
        for j in -1,0,1:
            if (i == 0 and j == 0):
                continue

            x2, y2 = x1 + i, y1 + j

            if not ((0 <= x2 < N) and (0 <= y2 < N)):
                continue

            c = board[x2][y2]

            if (move_type == 'm' and c == '.') or (move_type != 'm' and c != player and c != '.'):
                net += 1

    return net

def main():
    from sys import argv
    data = argv[1]

    player, board = data.split(',', 1)
    board = map(list, board.split(','))
    N = len(board)

    all_pos_gen = ((a,b) for a in range(N) for b in range(N) if board[a][b] == player)
    all_move_gen = (move for pos in all_pos_gen for move in gen_moves(board, pos))
    move = max(all_move_gen, key=lambda move: eval_move(board, move))

    print move[1], move[2], move[3], move[4]

if __name__ == "__main__":
    main()

Chạy ví dụ (sử dụng ví dụ được đưa ra trong mô tả thử thách và giả sử mã được lưu trong tệp có tên slime.py):

$ python slime.py 3,11111222,11111444,11.22444,.1222.4.,333.3244,33333.44,333...44,333....4
4 0 2 2

3

Màu xanh nông

Màu xanh nông cố gắng tìm hiểu những gì có thể xảy ra trong tương lai bằng cách thực hiện tìm kiếm toàn bộ cây di chuyển có thể, không may, anh ta không thể tiến xa hơn đến lượt tiếp theo. Sau đó, anh ta tát một số điểm nửa khẳng định trên mỗi ván có thể sau lượt tiếp theo của mình, tính điểm cho từng nhánh riêng lẻ với một công thức vô lý không kém và: voila di chuyển lý tưởng được biết đến!

EDIT: Mã ban đầu chạy waay quá chậm nên tôi đã sửa đổi nó để nó chỉ lấy một mẫu ngẫu nhiên của tất cả các di chuyển có thể. Nó sẽ thử gần như tất cả các động tác khi có ít di chuyển có thể và một tỷ lệ nhỏ hơn khi có nhiều di chuyển hơn có thể.

import java.awt.Point;  

    public class ShallowBlue {
        private static final int MAX_ROUNDS = 5, PLAYERS = 4;
        static int me = 0;

        public static void main(String[] args) {
            if (args[0] == null) {
                return;
            }

            me = Integer.parseInt(args[0].split(",", 2)[0]);
    String board = args[0].split(",", 2)[1];

    System.out.println(getBestMove(board, me, MAX_ROUNDS - 1));
}

private static String getBestMove(String board, int player, int rounds) {
    String [] boards = new String[24];
    int checkedBoards = 1;
    char playerChar = Integer.toString(player).charAt(0);
    String tempMove = getMove(0, 0, 0, 0);
    String tempBoard = calculateMove(board, tempMove); 
    boards[0] = tempBoard;
    String bestMove = tempMove;
    double us = numberOfUs(board, playerChar); 
    double skip = (us*2.5/(us*2.5 + 1))/4 + 0.735;
    if (rounds == MAX_ROUNDS - 2) {
        skip = skip*skip;
    }

    float bestScore, worstScore, averageScore, tempScore;
    int scores;

    if (rounds == 0) {
        tempScore = calculateScore(tempBoard, MAX_ROUNDS - rounds - 1);
    } else {
        tempScore = getScore(getBestMove(tempBoard, player%PLAYERS + 1, rounds - 1));
    }

    scores = 1;
    bestScore = tempScore;
    worstScore = tempScore;
    averageScore = tempScore;

    for (int x = 0; x < 8; x++) {
        for (int y = 0; y < 8; y++) {
            if (getCharAt(board, x, y) == playerChar) {
                Point[] possibleMergers = getNeighboringMatches(board, new Point(x, y), playerChar);
                if (possibleMergers[0] != null) {
                    tempMove = getMove(possibleMergers[0].x, possibleMergers[0].y, x, y); 
                    tempBoard = calculateMove(board, tempMove);
                    if (addIfUnique(boards, tempBoard, checkedBoards)) {
                        checkedBoards++;
                        if ((rounds != MAX_ROUNDS - 1) && (rounds == 0 || Math.random() < skip)) {
                            tempScore = calculateScore(tempBoard, MAX_ROUNDS - rounds - 1);
                        } else {
                            tempScore = getScore(getBestMove(tempBoard, player%PLAYERS + 1, rounds - 1));
                        }

                        if (tempScore > bestScore) {
                            bestMove = tempMove;
                        }
                        bestScore = Math.max(tempScore, bestScore);
                        worstScore = Math.min(tempScore, worstScore);

                        scores++;
                        averageScore = (averageScore*(scores - 1) + tempScore)/scores;
                    }
                }
            } else if (getCharAt(board, x, y) == '.') {
                Point[] possibleSpreaders = getNeighboringMatches(board, new Point(x, y), playerChar);
                int i = 0;
                while (i < possibleSpreaders.length && possibleSpreaders[i] != null) {
                    tempMove = getMove(possibleSpreaders[i].x, possibleSpreaders[i].y, x, y); 
                    tempBoard = calculateMove(board, tempMove);
                    if ((rounds != MAX_ROUNDS - 1) && (rounds == 0 || Math.random() < skip)) {
                        tempScore = calculateScore(tempBoard, MAX_ROUNDS - rounds - 1);
                    } else {
                        tempScore = getScore(getBestMove(tempBoard, player%PLAYERS + 1, rounds - 1));
                    }

                    if (tempScore > bestScore) {
                        bestMove = tempMove;
                    }
                    bestScore = Math.max(tempScore, bestScore);
                    worstScore = Math.min(tempScore, worstScore);

                    scores++;
                    averageScore = (averageScore*(scores - 1) + tempScore)/scores;

                    i++;
                }
                Point[] possibleJumpers = getNextNeighboringMatches(board, new Point(x, y), playerChar);
                i = 0;
                while (i < possibleJumpers.length && possibleJumpers[i] != null) {
                    tempMove = getMove(possibleJumpers[i].x, possibleJumpers[i].y, x, y); 
                    tempBoard = calculateMove(board, tempMove);
                    if ((rounds != MAX_ROUNDS - 1) && (rounds == 0 || Math.random() < skip)) {
                        tempScore = calculateScore(tempBoard, MAX_ROUNDS - rounds - 1);
                    } else {
                        tempScore = getScore(getBestMove(tempBoard, player%PLAYERS + 1, rounds - 1));
                    }

                    if (tempScore > bestScore) {
                        bestMove = tempMove;
                    }
                    bestScore = Math.max(tempScore, bestScore);
                    worstScore = Math.min(tempScore, worstScore);

                    scores++;
                    averageScore = (averageScore*(scores - 1) + tempScore)/scores;

                    i++;
                }
            }
        }
    }

    if (rounds == MAX_ROUNDS - 1) {
        return (bestMove);
    } else {
        return getScoreString(bestScore, worstScore, averageScore);
    }
}

private static int numberOfUs(String board, char playerChar) {
    int us = 0;

    for (int i = 0; i < board.length(); i++ ) {
         if (board.charAt(i) == playerChar) {
            us++;
        }
    }

    return us;
}

private static float calculateScore(String board, int roundsPassed) {
    int empties = 0;
    int us = 0;
    int enemy1 = 0;
    int enemy2 = 0;
    int enemy3 = 0;
    for (int i = 0; i < board.length(); i++ ) {
        if (board.charAt(i) == '.') {
            empties++;
        } else if (board.charAt(i) == Integer.toString(me).charAt(0)) {
            us++;
        } else if (board.charAt(i) == Integer.toString(me%PLAYERS + 1).charAt(0)) {
            enemy1++;
        } else if (board.charAt(i) == Integer.toString(me%PLAYERS + 2).charAt(0)) {
            enemy2++;
        } else if (board.charAt(i) == Integer.toString(me%PLAYERS + 3).charAt(0)) {
            enemy3++;
        }
    }

    if (us != 0) {
        us += roundsPassed;
    }

    if (enemy1 != 0) { 
        enemy1 = enemy1 + (roundsPassed + 3)%PLAYERS;
    }

    if (enemy2 != 0) { 
        enemy2 = enemy2 + (roundsPassed + 2)%PLAYERS;
    }

    if (enemy3 != 0) { 
        enemy3 = enemy3 + (roundsPassed + 1)%PLAYERS;
    }

    return us*(empties + 1)/(Math.max(Math.max(enemy1, enemy2), enemy3) + 1);
}

private static float getScore(String scoreString) {
    float bestScore, worstScore, averageScore;
    String[] scores = new String[3];

    scores = scoreString.split(",");
    bestScore = Float.parseFloat(scores[0]);
    worstScore = Float.parseFloat(scores[1]);
    averageScore = Float.parseFloat(scores[2]);


    return (float) Math.sqrt(Math.sqrt(bestScore*averageScore*worstScore*worstScore));
}

private static String getScoreString(float bestScore, float worstScore, float averageScore) {
    return Float.toString(bestScore) + ',' + Float.toString(worstScore) + ',' + Float.toString(averageScore);
}

private static boolean addIfUnique(String[] boards, String board, int checkedBoards) {
    int i = 0;

    while (i < boards.length && boards[i] != null) {
        if (boards[i].equals(board)) {
            return false;
        }
        i++;
    }

    if (i < boards.length) {
        boards[i] = board;
    } else {
        boards[checkedBoards%boards.length] = board;
    }

    return true;
}

private static String calculateMove(String board, String move) {
    int x1 = Integer.parseInt(Character.toString(move.charAt(0)));
    int y1 = Integer.parseInt(Character.toString(move.charAt(2)));
    int x2 = Integer.parseInt(Character.toString(move.charAt(4)));
    int y2 = Integer.parseInt(Character.toString(move.charAt(6)));

    if ((Math.abs(y1 - y2) == 2 || Math.abs(x1 - x2) == 2) 
            &&  getCharAt(board, x2, y2) == '.') {
        Point[] enemies = new Point[8];

        enemies = getNeighboringEnemies(board, new Point(x1, y1), Integer.parseInt(Character.toString(getCharAt(board, x1, y1))));

        board = replace(board, enemies, getCharAt(board, x1, y1));
        Point[] middle = {new Point(x1, y1)};
        board = replace(board, middle, '.');
    }

    if ((Math.abs(y1 - y2) == 1 || Math.abs(x1 - x2) == 1)) { 
        if (getCharAt(board, x2, y2) == '.' || getCharAt(board, x1, y1) == getCharAt(board, x2, y2)) {
            boolean merge = true;
            if (getCharAt(board, x2, y2) == '.') {
                merge = false;
            }

            Point[] spaces = new Point[8];
            spaces = getNeighboringMatches(board, new Point(x1, y1), '.');
            board = replace(board, spaces, getCharAt(board, x1, y1));

            if (merge) {
                Point[] source = {new Point(x1, y1)};
                board = replace(board, source, '.');
            }
        }
    }

    return board;
}

private static String replace(String board, Point[] targets, char source) {
    int i = 0;

    while (i < targets.length && targets[i] != null) {
        if (targets[i].x == 7 && targets[i].y == 7) {
            board = board.substring(0, getIndexAt(targets[i].x, targets[i].y)) + source;
        } else if (targets[i].x == 0 && targets[i].y == 0) {
            board = source + board.substring(getIndexAt(targets[i].x, targets[i].y) + 1);
        } else {
            board = board.substring(0, getIndexAt(targets[i].x, targets[i].y)) + source + board.substring(getIndexAt(targets[i].x, targets[i].y) + 1);
        }
        i++;
    }

    return board;
}

private static Point[] getNeighboringMatches(String board, Point coord, char match) {
    Point[] matches = new Point[8];

    int i = 0;
    for (int x = coord.x - 1; x <= coord.x + 1; x++) {
        for (int y = coord.y - 1; y <= coord.y + 1; y++) {
            if ((y != coord.y || x != coord.x ) && getCharAt(board, x, y) == match){
                matches[i] = new Point(x, y);
                i++;
            }
        }
    }

    return matches;
}

private static Point[] getNeighboringEnemies(String board, Point coord, int player) {
    Point[] enemies = new Point[8];

    for (int i = 1; i <= PLAYERS; i++){
        enemies = mergeArr(enemies, getNeighboringMatches(board, coord, Integer.toString((player + i - 1)%PLAYERS + 1).charAt(0)));
    }

    return enemies;
}

private static Point[] getNextNeighboringMatches(String board, Point coord, char match) {
    Point[] matches = new Point[16];

    int i = 0;
    for (int x = coord.x - 2; x <= coord.x + 2; x++) {
        for (int y = coord.y - 2; y <= coord.y + 2; y++) {
            if ((Math.abs(y - coord.y) == 2 || Math.abs(x - coord.x) == 2) && getCharAt(board, x, y) == match){
                matches[i] = new Point(x, y);
                i++;
            }
        }
    }

    return matches;
}

private static char getCharAt(String board, int x, int y) {

    if (x >= 0 && x < 8 && y >= 0 && y < 8) {
        return board.charAt(9*x + y);
    } else {
        return '\0';
    }
}

private static int getIndexAt(int x, int y) {
    return 9*x + y;
}

private static Point[] mergeArr(Point[] arr1, Point[] arr2) {
    int i = 0;
    int j = 0;

    while (i < arr1.length && arr1[i] != null) {
        i++;
    }

    while (j < arr2.length && arr2[j] != null) {
        arr1[i + j] = arr2[j];
        j++;
    }

    return arr1;
}

private static String getMove(int x1, int y1, int x2, int y2) {
    return Integer.toString(x1) + " " + Integer.toString(y1) + " " + Integer.toString(x2) + " " + Integer.toString(y2);
    }
}

Tôi không phải là một lập trình viên và cách tiếp cận này phức tạp hơn tôi dự đoán, tôi chưa thử nghiệm mã này (lúc này) vì đã 3 giờ sáng và tôi cần phải đi làm sớm vào ngày mai. Rất có thể bot của tôi sẽ hết thời gian hoặc hoàn toàn không hoạt động. Tôi cũng có thể đã nhận được tọa độ sai, tôi sẽ có một cái nhìn khác vào ngày mai với đôi mắt mới, nhưng một cặp (hoặc nhiều hơn) luôn được chào đón, đó là một đôi mắt (có nhiều kinh nghiệm hơn).
overactor

Tôi đang nhận được một loạt các trường hợp ngoại lệ cho điều này (xem trò chuyện cho stacktrace).
Doorknob

Mã của tôi cuối cùng đã chạy, nhưng tôi rất cần tăng tốc độ để tôi có thể giảm tỷ lệ các nhánh tôi bỏ qua. Có ai biết làm thế nào để cải thiện mớ hỗn độn này?
overactor

Vì nó không được đánh gôn, bạn có thể đăng nó lên Code Review ... Điều đó có nằm trong quy tắc hay nó sẽ bị nhăn mặt?
trichoplax

1
Tôi đã thấy câu trả lời này hai ngày trước, nhưng tôi mới nhận ra rằng "Shallow Blue" là một cách chơi chữ của "Deep Blue" nổi tiếng.
ngay

2

Nhảy

Thích nhảy, thậm chí nhiều hơn về phía giữa.

Sẽ vượt qua nếu không có slime có thể nhảy.

C ++ , nên biên dịch đơn giản vớig++ jumper.cpp -o jumper

#include <math.h>
#include <algorithm>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#define maxn(x, y) ((x) > (y) ? (x) : (y))
#define absn(x) ((x) < 0 ? -(x) : (x))
class Board {
 public:
    Board(std::string input_string);
    void Move();
 private:
    void ParseBoardState(std::string console_string);
    int Slimes(int cell);
    void GetXY(int cell, int& r, int& c);
    bool CanJumpFromHere(int cell, int& jump_to_cell, int& rad);
    int CalcRadius(int cell);
    bool CheckJumpDist(int x, int y);

    int player_num_;
    std::size_t board_dim_;
    std::size_t sq_;
    std::vector< std::vector<int> > slimes_;
};
Board::Board(std::string input_string) 
    : player_num_(0), 
      board_dim_(0),
      slimes_() {
    board_dim_ = std::count(input_string.begin(), input_string.end(), ',');
    sq_ = board_dim_ * board_dim_;
    std::istringstream temp(input_string.substr(0,1));
    temp >> player_num_;
    ParseBoardState(input_string);
}
void Board::ParseBoardState(std::string console_string) {
    int place = 0;
    for (std::size_t row = 0; row < board_dim_; ++row ) {
        slimes_.push_back(std::vector<int>());
        place = console_string.find(",",place+1);
        std::string temp2 = console_string.substr(place+1, 8);
        for (std::size_t col = 0; col < board_dim_; ++col ) {
            int sl = 0;
            std::istringstream bint(temp2.substr(col,1));
            bint >> sl;
            slimes_[row].push_back(sl);
        }
    }
}
int Board::Slimes(int cell) {
    int r = 0;
    int c = 0;
    GetXY(cell, r, c);
    return  slimes_[r][c];
}
void Board::GetXY(int cell, int& r, int& c) {
    for (std::size_t row = 0; row < board_dim_; ++row ) {
        for (std::size_t col = 0; col < board_dim_ ; ++col ) {
            if ( (row * board_dim_ + col) == cell) {
                r = row;
                c = col;
            }
        }
    }
}
void Board::Move() {

    // go through each cell:
    int index = 0;
    int jump_to_cell = 0;
    int rad = 0;
    int min_rad = 1000;
    int best_jump_to = -1;
    int best_jump_from = -1;
    for (int c = 0; c < sq_; ++c) {
        if (Slimes(c) == player_num_) {
            if (CanJumpFromHere(c, jump_to_cell , rad)) {
                if (rad < min_rad) {
                    best_jump_from = c;
                    best_jump_to = jump_to_cell;
                    min_rad = rad;
                }
                index += 1;
            }
        }
    }

    int ret_row = 0;
    int ret_col = 0;

    if (index == 0) {
        // can't jump so dont bother:
        std::cout << "0 0 0 0" << std::endl;
    } else {
        GetXY(best_jump_from, ret_row, ret_col);
        std::cout << ret_row << " " << ret_col  << " ";
        GetXY(best_jump_to, ret_row, ret_col);
        std::cout << ret_row << " " << ret_col << std::endl;
    }
}
bool Board::CanJumpFromHere(int cell, int& ret_jump_to_cell, int & ret_rad) {
    int r = 0;
    int c = 0;
    int rad = 10000;
    int jump_to_cell = 0;
    int rad_min_for_this_cell = 10000;
    GetXY(cell, r, c);
    bool jumpable = false;
    for (int row_test = -2; row_test < 3; ++row_test) {
        for (int col_test = -2; col_test < 3; ++col_test) {
            if ( (r + row_test) > 0 &
                 (r + row_test) < board_dim_ &&
                 (c + col_test) > 0 &&
                 (c + col_test) < board_dim_ &&
                 (CheckJumpDist(col_test, row_test)) &&
                 (slimes_[r+row_test][c+col_test] == 0)) {

                jumpable = true;
                jump_to_cell = (r + row_test) * board_dim_ + c + col_test;
                rad = CalcRadius(jump_to_cell);

                if (rad < rad_min_for_this_cell) {
                    ret_rad = rad;
                    ret_jump_to_cell = jump_to_cell;
                    rad_min_for_this_cell = ret_rad;
                }
            }
        }
    }
    return jumpable;
}
bool Board::CheckJumpDist(int x, int y) {
    int maxDelta = maxn(absn(x), absn(y));
    if (maxDelta <= 0 || maxDelta > 2) {
        return false;
    } else {
        return true;
    }
}
int Board::CalcRadius(int cell) {
    int r = 0;
    int c = 0;
    GetXY(cell, r, c);
    // unnecessary accuracy considering how bad this bot is:
    float mid = static_cast<float>(board_dim_) / 2;
    float rad = sqrt((r - mid) * (r - mid) + (c-mid)*(c-mid));
    int ret = static_cast<int>(rad + 0.5);
    return ret;
}
int main(int argc, char* argv[]) {
    if (argc != 2) {
        return 0;
    } else {
        std::string input_string(argv[1]);
        Board board(input_string);
        board.Move();
    }
    return 0;
}

Tôi đã đánh cắp xác minh di chuyển của bạn xin lỗi. Ngoài ra, tôi đã từ bỏ các thực hành mã hóa chính xác ngay sau khi tôi bắt đầu, vì vậy đừng nhìn. Tuy nhiên, nó dường như chạy trên bất kỳ kích thước của bảng.


2

Tử thần :

Mô tả : Cố gắng săn lùng kẻ thù yếu nhất và tiêu diệt chúng. Nói lại.

Cách chạy : ruby ​​DeathSlime.rb

Phiên bản Ruby : 2.1.2

#!/usr/bin/env ruby
class PlayerPosition;
  attr_accessor :x, :y;
  def initialize(x, y) @x = x; @y = y; end
  def distance(pos) Math.sqrt((pos.x - @x)**2 + (pos.y - @y)**2); end
end

class Board
  attr_reader :player, :empty_positions
  def initialize(player_id, game_state_string)
    @player_positions = {}
    @empty_positions = []

    @enemies = []
    @player = Player.new

    row = 0
    col = 0
    game_state_string.chars.each do |tile|
      row += 1 and col = 0 and next if tile == ','
      @empty_positions << PlayerPosition.new(col, row) and col += 1 and next if tile == '.'

      @player_positions[tile] ||= []
      @player_positions[tile] << PlayerPosition.new(col, row)
      col += 1
    end

    @player_positions.each do |id, positions|
      @enemies << Player.new(id, positions) if id != player_id
      @player = Player.new(id, positions) if id == player_id
    end
  end

  def border_space(player_positions, possible_border, allowance = 1)
    near = []
    possible_border.each do |border|
      is_near = false
      player_positions.each {|pos| is_near = true and break if pos.distance(border) <= allowance}
      near << border if is_near
    end
    near
  end

  def closest_to(player_positions, enemy_positions)
    player_closest_block = nil
    shortest_distance = 1000
    enemy_closest_block = nil
    player_positions.each do |player|
      enemy_positions.each do |enemy|
        if player.distance(enemy) < shortest_distance
          shortest_distance = player.distance(enemy)
          enemy_closest_block = enemy
          player_closest_block = player
        end
      end
    end
    return player_closest_block, enemy_closest_block
  end

  def empty_space_near(player_positions, allowance = 1); border_space(player_positions, @empty_positions, allowance); end
  def weakest_enemy; @enemies.select{|enemy| !enemy.dead? }.sort {|x,y| x.strength <=> y.strength}.first; end
end

class Player
  attr_reader :positions
  def initialize(id = -1, positions = []); @id = id; @positions = positions; end
  def dead?; @positions.length == 0; end
  def strength; @positions.length; end
  def can_hurt?(enemy)
    is_close_enough = false
    self.positions.each do |my_pos|
      enemy.positions.each {|enemy_pos| is_close_enough = true and break if my_pos.distance(enemy_pos) <= 2 }
    end
    is_close_enough
  end
end




class DeathSlime

  def initialize(arg_string)
    game_state = arg_string[2..-1]
    player_id = arg_string[0]
    @board = Board.new(player_id, game_state)
  end

  def attack
    if @board.weakest_enemy
      try_to_spread_to_weakest || try_to_jump_to_weakest || try_to_merge_to_weakest || try_to_move_to_weakest
    else
      try_to_move if @empty_positions.length > 0
    end
  end


  def try_to_spread_to_weakest
    mine = @board.empty_space_near(@board.player.positions, 1)
    theirs = @board.empty_space_near(@board.weakest_enemy.positions, 1)
    target_space = mine.detect{|space| theirs.include?(space) }
    return move(@board.closest_to(@board.player.positions, [target_space]).first, target_space) if target_space
    false
  end

  def try_to_jump_to_weakest
    mine = @board.empty_space_near(@board.player.positions, 2)
    theirs = @board.empty_space_near(@board.weakest_enemy.positions, 1)
    target_space = mine.detect{|space| theirs.include?(space) }
    return move(@board.closest_to(@board.player.positions, [target_space]).first, target_space) if target_space
    false
  end

  def try_to_merge_to_weakest
    definite_border = nil
    definite_merge = nil
    possible_border = @board.border_space(@board.weakest_enemy.positions, @board.player.positions)
    possible_border.each do |border|
      possible_merges = @board.border_space([ border ], @board.player.positions.select{|space| space != border })
      definite_merge = possible_merges.first and definite_border = border and break if possible_merges.length > 0
    end
    return move(definite_merge, definite_border) if definite_border && definite_merge
    false
  end

  def try_to_move_to_weakest
    player_closest, enemy_closest = @board.closest_to(@board.player.positions, @board.weakest_enemy.positions)
    spreading_distance = @board.empty_space_near([player_closest], 1)
    jumping_distance = @board.empty_space_near([player_closest], 2)
    theirs = @board.empty_space_near(@board.player.positions, 2)

    spreading_space = spreading_distance.detect{|space| theirs.include?(space) }
    return move(@board.closest_to(@board.player.positions, [spreading_space]).first, spreading_space) if spreading_space

    jumping_space = jumping_distance.detect{|space| theirs.include?(space) }
    return move(@board.closest_to(@board.player.positions, [jumping_space]).first, jumping_space) if jumping_space

    return move(@board.closest_to(@board.player.positions, [spreading_distance]).first, spreading_distance) if spreading_distance.length > 0
    return move(@board.closest_to(@board.player.positions, [jumping_distance]).first, jumping_distance) if jumping_distance.length > 0

    #merge randomly
    closest_enemy = @board.closest_to(@board.player.positions, @board.weakest_enemy.positions).first
    return move(@board.closest_to(@board.player.positions.select{|space| space != closest_enemy }, [closest_enemy]).first, closest_enemy)
  end

  def try_to_move
    spreading_distance = @board.empty_space_near(board.player.positions, 1)
    jumping_distance = @board.empty_space_near(board.player.positions, 2)

    return move(@board.closest_to(@board.player.positions, [spreading_distance]).first, spreading_distance) if spreading_distance.length > 0
    return move(@board.closest_to(@board.player.positions, [jumping_distance]).first, jumping_distance) if jumping_distance.length > 0
  end

  def move(start_block, end_block)
    STDOUT.write "#{start_block.x} #{start_block.y} #{end_block.x} #{end_block.y}"
    true
  end
end

slime_of_death = DeathSlime.new(ARGV[0])
slime_of_death.attack

1

Địa y

Đây là một bot được viết bằng R. Nó cần được kích hoạt bằng cách sử dụng Rscript Lichen.R.

input <- strsplit(commandArgs(TRUE),split=",")[[1]]
me <- input[1]
arena <- do.call(rbind,strsplit(input[-1],""))
n <- sum(arena==me)
where <- which(arena==me,arr.ind=TRUE)
closest <- function(a,b){
    x <- abs(outer(a[,1],b[,1],`-`))
    y <- abs(outer(a[,2],b[,2],`-`))
    matrix(which(x<2&y<2,arr.ind=TRUE),ncol=2)
    }
if(n==0){ #No slime on the board
    out <- "0 0 0 0"
    }else if(n==1){ #One slime on the board
        x <- where[1]+c(1,-1)
        y <- where[2]+c(1,-1)
        out <- paste(where[1]-1,where[2]-1,x[x%in%2:(nrow(arena)-1)]-1,y[y%in%2:(nrow(arena)-1)]-1,sep=" ")
    }else{
        area <- apply(which(arena==me,arr.ind=TRUE),2,range,na.rm=TRUE)
        empty <- matrix(which(arena==".",arr.ind=TRUE),ncol=2)
        opponents <- c("1","2","3","4")[c("1","2","3","4")!=me]
        for(i in seq_along(opponents)){
            if(i==1){
                other <- which(arena==opponents[i],arr.ind=TRUE)
                }else{other <- rbind(other,which(arena==opponents[i],arr.ind=TRUE))}
            }
        fillable <- matrix(empty[empty[,1]%in%area[1,1]:area[2,1]&empty[,2]%in%area[1,2]:area[2,2],],ncol=2)
        enemies <- matrix(other[other[,1]%in%area[1,1]:area[2,1]&other[,2]%in%area[1,2]:area[2,2],],ncol=2)
        if(length(unique(where[,2]))==1 | length(unique(where[,2]))==1){ #Slimes form a line
            W <- closest(where,empty)
            if(nrow(W)){
                out <- paste(c(where[W[1,1],]-1,empty[W[1,2],]-1),collapse=" ")
            }else{out <- "0 0 0 0"}
        }else if(length(enemies)&length(fillable)){ #There are enemies and empty spaces in habitable area
            w <- closest(enemies, fillable)
            if(nrow(w)){
                X <- abs(where[,1]-fillable[w[1,2],1])
                Y <- abs(where[,2]-fillable[w[1,2],2])
                W <- which(X<2&Y<2)
                out <- paste(c(where[W[1],]-1,fillable[w[1,2],]-1),collapse=" ")
            }else{out <- "0 0 0 0"}
        }else if(length(fillable)){ #There are empty spaces in habitable area
            w <- closest(fillable,where)
            out <- paste(c(where[w[1,2],]-1,fillable[w[1,1],]-1),collapse=" ")
        }else{
            x <- area[!area[,1]%in%c(1,nrow(arena)),1]
            y <- area[!area[,2]%in%c(1,ncol(arena)),2]
            if(sum(arena[x+(-1:1),y+(-1:1)]==".")>1){
                w <- where[where[,1]%in%(x+c(1,-1))&where[,2]%in%(y+c(1,-1)),]
                out <- paste(w[1]-1,w[2]-1,x-1,y-1,sep=" ")
            }else{
                W <- closest(where, empty)
                if(nrow(W)){
                    out <- paste(c(where[W[1,1],]-1,empty[W[1,2],]-1),collapse=" ")
                }else{out <- "0 0 0 0"}
            }
        }
    }
cat(out)

Thuật toán dự định là nó cố gắng bao phủ một khu vực hình chữ nhật (điền vào chỗ trống bằng cách sử dụng spread ). Khi hình chữ nhật hoàn thành, mergeshai slime tại một trong các góc của nó (một góc xa nhất từ ​​góc của bảng) để mở rộng khu vực "có thể ở được", sau đó điền vào hình chữ nhật mới được xác định, v.v. Nó không sử dụng jump.

.....   .....   .....   .....   .....   ..333
.....   .333.   3333.   3333.   3333.   33333
333..   3333.   3333.   3333.   3333.   33.33
333..   3.33.   3.33.   3333.   3333.   3333.
333..   333..   333..   333..   3333.   3333.

Nếu kẻ thù ở trong khu vực có thể ở được và cũng có một khoảng trống trong khu vực, nó sẽ lấp đầy khoảng trống bên cạnh nó. Nếu chất nhờn nên được sáp nhập vào khi mở rộng khu vực có thể ở được bao quanh bởi kẻ thù, thì một chất nhờn sẽ chỉspread thay vì chất này hợp nhất.


Tôi đang nhận được một loạt các lỗi cho việc này (xem trò chuyện về stacktrace).
Doorknob

Bot bây giờ gửi 0 0 0 0khi không còn chất nhờn trên tàu.
plannapus

0

Góc tối

Chất nhờn này có một khái niệm về các góc, hoặc ít nhất là khi tôi viết nó lần đầu tiên bằng C #, tôi không thực sự chắc chắn nữa.

Được viết bằng C ++, có lẽ sẽ biên dịch tốt với gcc và bên cạnh không có đối số; hy vọng tôi đã không sử dụng bất cứ thứ gì MSVC cụ thể một cách tình cờ.

Đã thử nghiệm độc quyền trên máy chủ đã sửa đổi (không có trình biên dịch C ++ mới lạ nào) vì vậy tôi không biết nó sẽ hoạt động như thế nào, hy vọng nó sẽ không bị loại vì quá chậm. Hiện tại không có sự ngẫu nhiên trong bot này, nhưng tôi có thể thêm một số vào một ngày sau đó.

Đây là C # được chuyển sang C ++ (do lo ngại về tốc độ) bởi một người không thực sự biết về C ++ và rất tệ. Nó bắt đầu bằng cách xây dựng một loạt các Tế bào, sau đó nó lấp đầy tất cả các loại thông tin không có ích về các tế bào xung quanh nó (số lượng tế bào của tôi, số lượng slime của tôi, loại điều đó). Sau đó, nó sử dụng thông tin này để quyết định xem có cần xem xét kỹ hơn thông tin đã sử dụng để tạo thông tin đó hay không, và sau đó có khả năng sử dụng thông tin đã nói để tạo ra một đầu ra có ý nghĩa.

#include <iostream>

#define min(a,b) a>b?b:a;
#define max(a,b) a>b?a:b;

#define null 0 // fun times

struct Cell
{
public:
    int t;
    int x, y;
    int counts1[5];
    int counts2[5];
    int ecount1;
    int ecount2;
    bool safe1;
    bool safe2;

    bool canspread;
    bool canjump;
    bool canmerge;

    bool spreadable;
    bool jumpable;
    bool mergeable;

    Cell()
    {
        for (int i = 0; i < 5; i++)
        {
            counts2[i]=counts1[i]=0;
        }
        ecount1=ecount2=0;
        safe1=safe2=mergeable=jumpable=spreadable=canmerge=canjump=canspread=false;
    }

    Cell(int tN, int xN, int yN) // not sure why I can't call () constructor here
    {
        for (int i = 0; i < 5; i++)
        {
            counts2[i]=counts1[i]=0;
        }
        ecount1=ecount2=0;
        safe1=safe2=mergeable=jumpable=spreadable=canmerge=canjump=canspread=false;

        t = tN;
        x = xN;
        y = yN;
    }

    void findOptions(int moi)
    {
        if (t == 0)
        {
            if (counts1[moi] > 0)
                spreadable = true;
            if (counts2[moi] > 0)
                jumpable = true;
        }
        else if (t == moi)
        {
            if (counts1[moi] > 0)
                mergeable = canmerge = true;
            if (counts1[0] > 0)
                canspread = true;
            if (counts2[0] > 0)
                canjump = true;
        }
    }
};

const int dim = 8;
const int hdim = 4;

int moi;
int chezMoi;

int target;
int chezTarget;

Cell cells[dim][dim];

int cornerCounts[4][5];
int totalCounts[5];

// ring ness - why why why

// end ring ness

int tlx;
int tly;
int thx;
int thy;

int alx;
int aly;
int ahx;
int ahy;

int rj;
int rstate;

void ring(int x, int y, int dist)
{   
    tlx=x-dist;
    tly=y-dist;
    thx=x+dist;
    thy=y+dist;

    alx=max(0, tlx);
    aly=max(0, tly);
    ahx=min(dim-1, thx);
    ahy=min(dim-1, thy);

    rstate = 0;
}

bool nextR(Cell** outc)
{
    if (rstate == 1)
    {
        goto state1;
    }
    if (rstate == 2)
    {
        goto state2;
    }
    if (rstate == 3)
    {
        goto state3;
    }
    if (rstate == 4)
    {
        goto state4;
    }

    if (alx == tlx)
    {
        rj = aly - 1;
        rstate = 1;
    }
state1:
    if (alx == tlx)
    {
        if (++rj <= ahy)
        {
            *outc = (cells[alx]+rj);
            return true;
        }
        alx++;
    }

    if (ahx == thx)
    {
        rj = aly - 1;
        rstate = 2;
    }
state2:
    if (ahx == thx)
    {
        if (++rj <= ahy)
        {
            *outc = (cells[ahx]+rj);
            return true;
        }
        ahx--;
    }

    if (aly == tly)
    {
        rj = alx - 1;
        rstate = 3;
    }
state3:
    if (aly == tly)
    {
        if (++rj <= ahx)
        {
            *outc = (cells[rj]+aly);
            return true;
        }
    }

    if (ahy == thy)
    {
        rj = alx - 1;
        rstate = 4;
    }
state4:
    if (ahy == thy)
    {
        if (++rj <= ahx)
        {
            *outc = (cells[rj]+ahy);
            return true;
        }
    }

    return null;
}

int cox;
int coy;

int ci;
int cj;

void corner(int idx)
{
    cox = (idx / 2) * hdim;
    coy = (idx % 2) * hdim;

    ci = 0;
    cj = -1;
}

bool nextC(Cell** outc)
{
    for (;ci < hdim;ci++)
    {
        for (;++cj < hdim;)
        {
            *outc = (cells[ci+cox]+cj+coy);
            return true;
        }
        cj = -1;
    }

    return false;
}

void cornerCount(int idx, int* c)
{
    int ox = (idx / 2) * hdim;
    int oy = (idx % 2) * hdim;

    for (int i = 0; i < hdim; i++)
    {
        for (int j = 0; j < hdim; j++)
        {
            c[cells[i+ox][j+oy].t]++;
        }
    }
}

void ringCount(int x, int y, int dist, int* c)
{
    int tlx=x-dist;
    int tly=y-dist;
    int thx=x+dist;
    int thy=y+dist;

    int alx=max(0, tlx);
    int aly=max(0, tly);
    int ahx=min(dim-1, thx);
    int ahy=min(dim-1, thy);

    if (alx == tlx)
    {
        for (int j = aly; j <= ahy; j++)
            c[cells[alx][j].t]++;
        alx++;
    }
    if (ahx == thx)
    {
        for (int j = aly; j <= ahy; j++)
            c[cells[ahx][j].t]++;
        ahx--;
    }
    if (aly == tly)
    {
        for (int i = alx; i <= ahx; i++)
            c[cells[i][aly].t]++;
    }
    if (ahy == thy)
    {
        for (int i = alx; i <= ahx; i++)
            c[cells[i][ahy].t]++;
    }
}

int trans(char c)
{
    return c<48?0:c-48;
}

std::string res(Cell* ca, Cell* cb)
{
    char buff[100]; // shhh
    sprintf_s(buff, "%d %d %d %d\n", ca->x, ca->y, cb->x, cb->y);
    return std::string(buff);
}

std::string go(char* inp)
{
    moi = trans(inp[0]);

    int a = 2;

    for (int i = 0; i < dim; i++)
    {
        for (int j = 0; j < dim; j++)
        {
            cells[i][j] = Cell(trans(inp[a]), i, j);
            a++;
        }
        a++;
    }

    // count corners and totals
    for (int i = 0; i < 4; i++)
    {
        cornerCount(i, cornerCounts[i]);
        for (int j = 0; j < 5; j++)
        {
            totalCounts[j] += cornerCounts[i][j];
        }
    }

    // count and find cell options
    for (int i = 0; i < dim; i++)
    {
        for (int j = 0; j < dim; j++)
        {
            Cell* c = cells[i]+j;

            ringCount(i, j, 1, c->counts1);
            ringCount(i, j, 2, c->counts2);

            // safeness
            for (int r = 1; r < 5; r++)
            {
                if (r != moi)
                {
                    c->ecount1 += c->counts1[r];
                    c->ecount2 += c->counts2[r];
                }
            }
            c->safe1 = c->ecount1 == 0 && c->counts1[0] == 0; // surrounded by moi
            c->safe2 = c->ecount1 == 0 && c->ecount2 == 0; // no enemies in sight

            // that funcion which does stuff
            c->findOptions(moi);
        }
    }

    // find chezMoi
    chezMoi = moi-1; // might work, can't be bothered to work it out
    for (int i = 1; i < 4; i++)
    {
        if (cornerCounts[i][moi] > cornerCounts[chezMoi][moi])
            chezMoi = i;
    }

    int best = 0;

    if (cornerCounts[chezMoi][moi] < hdim * hdim)
    {
        // fill our corner
        best = 0;
        Cell* ac = null;
        Cell* bc = null;

        corner(chezMoi);
        Cell* c;
        while (nextC(&c))
        {
            if (c->spreadable && c->ecount1 + 1 > best)
            {
                ring(c->x, c->y, 1);
                Cell* oc;
                while (nextR(&oc))
                {
                    if (oc->canspread)
                    {
                        best = c->ecount1 + 1;
                        ac = oc;
                        bc = c;
                        break;
                    }
                }
            }
            if (c->mergeable && c->counts1[0] - 1 > best)
            {
                ring(c->x, c->y, 1);
                Cell* oc;
                while (nextR(&oc))
                {
                    if (oc->safe2 && oc->canmerge && c->counts1[0] > 0)
                    {
                        best = c->counts1[0] - 1;
                        ac = oc;
                        bc = c;
                        break;
                    }
                }
            }
        }

        if (bc != null)
        {
            return res(ac, bc);
        }
    }

    // pick target (why?)
    target = -1;
    best = 0;
    for (int i = 0; i < 4; i++)
    {
        if (i == moi)
            continue;
        int cur = totalCounts[i];
        if (target == -1 || cur > best)
        {
            target = i;
            best = cur; 
        }
    }

    if (target != -1)
    {
        for (int i = 0; i < 4; i++)
        {
            if (i == chezMoi)
                continue;
            int cur = cornerCounts[i][target];
            if (chezTarget == -1 || cur > best)
            {
                chezTarget = i;
                best = cur;
            }
        }

        // attack chosen target (not sure it does this anymore...)
        best = 0;
        Cell* ac = null;
        Cell* bc = null;

        for (int i = 0; i < dim; i++)
        {
            for (int j = 0; j < dim; j++)
            {
                Cell* c = cells[i]+j;

                if (c->spreadable && c->ecount1 + 1 > best)
                {
                    ring(c->x, c->y, 1);
                    Cell* oc;
                    while (nextR(&oc))
                    {
                        if (oc->canspread)
                        {
                            best = c->ecount1 + 1;
                            ac = oc;
                            bc = c;
                            break;
                        }
                    }
                }
                if (c->jumpable && c->ecount1 - 1 > best)
                {
                    ring(c->x, c->y, 2);
                    Cell* oc;
                    while (nextR(&oc))
                    {
                        if (oc->safe2 && oc->canjump)
                        {
                            best = c->ecount1 - 1;
                            ac = oc;
                            bc = c;
                            break;
                        }
                    }
                }
            }
        }

        if (bc != null)
        {
            return res(ac, bc);
        }
    }

    return "0 0 0 0\n";
}

int main(int argc, char* args[])
{
    printf(go(args[1]).c_str());
    return 0;
}
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.