Baba: la guerra del territorio

18

Eres una gota de baba. Naturalmente, al ser limo, debes rezumar la mayor cantidad de área posible. Pero hay otros 3 limos que quieren hacer exactamente lo mismo. ¿Quién será el limo superior?

Descripción

Todos los limos se reunirán en una arena. Los jueces (es decir, el programa de control) revisarán una lista exhaustiva de todas las combinaciones posibles de 4 limos, las colocarán en las esquinas de una mesa y observarán para ver qué limo se filtra en la mayor parte del área.

Sus limos pueden realizar una de 3 acciones cada turno: desplegarse, saltar o fusionarse. Se proporcionará una descripción más detallada de lo que significan en la sección Salida .

Junta / Arena

La arena será un tablero cuadrado (actualmente 8x8, pero esto puede cambiar en el futuro). Aquí hay un ejemplo de arena de un juego en progreso:

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

El limo está representado por los números del 1 al 4 (jugadores del 1 al 4), y el espacio vacío está representado por un punto ( .). Inicialmente, el tablero comienza como todo el espacio vacío, excepto por una sola unidad del limo del jugador 1 en la esquina superior izquierda, el jugador 2 en la esquina superior derecha, el jugador 3 en la esquina inferior izquierda y el jugador 4 en la esquina inferior derecha.

Las coordenadas están representadas por un índice de fila y columna basado en 0, para facilitar la lectura en el código. Por ejemplo, las coordenadas (3, 6) representan el séptimo cuadrado en la cuarta fila (en el ejemplo anterior, a 4). (Esto facilita el acceso a los cuadrados:. board[coords.x][coords.y]) Aquí hay una ilustración visual:

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

Entrada

La entrada de su programa será el jugador que sea (1, 2, 3 o 4), una coma ( ,), luego el contenido del tablero / arena (con nuevas líneas reemplazadas por comas). Por ejemplo, si fueras el jugador 3 en el escenario anterior, tu entrada sería:

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

Salida

Su programa debe generar 4 enteros. Los dos primeros son el índice de fila y columna, respectivamente, del limo que desea mover, y los siguientes dos son el índice de fila y columna de dónde desea moverlos.

Hay tres opciones que tiene en cada turno: desplegarse, saltar o fusionarse.

  • Untado

    Para extenderse, las coordenadas del objetivo deben estar exactamente a un cuadrado del limo que se está moviendo, y el cuadrado en las coordenadas del objetivo debe ser un espacio vacío. Al extenderse, se crea un nuevo limo en las coordenadas del objetivo y no se elimina el antiguo limo . Después de que se crea el nuevo limo, todos los limos enemigos en los 8 cuadrados alrededor de este nuevo limo se convierten en el jugador que se movió.

    Por ejemplo, con el tablero en la Fig. 1, si el jugador 1 fuera a salir 0 1 1 2, el resultado sería el tablero en la Fig. 2.

    1.         2.
      11.22      11.12
      1..22      1.112
      ..22.      ..11.
      .....      .....
    
  • Saltar

    Para saltar, las coordenadas del objetivo deben estar exactamente a dos cuadrados del limo que se está moviendo, y el cuadrado en las coordenadas del objetivo debe ser un espacio vacío. Al saltar, se crea un nuevo limo en las coordenadas del objetivo y se elimina el antiguo limo. Después de que se crea el nuevo limo, todos los limos enemigos en los 8 cuadrados alrededor de este nuevo limo se convierten en el jugador que se movió.

    Por ejemplo, con el tablero en la Fig. 1, si el jugador 1 fuera a salir 0 1 2 3, el resultado sería el tablero en la Fig. 2.

    1.         2.    
      11..2      1...2
      1...2      1...1
      ....2      ...11
      ...22      ...11
    
  • Unir

    Para fusionarse, las coordenadas del objetivo deben estar exactamente a un cuadrado del limo que se está moviendo, y el cuadrado en las coordenadas del objetivo debe ser el mismo limo del jugador. Al fusionar, se elimina el limo viejo. Luego, todos los espacios vacíos en los 8 cuadrados alrededor del limo objetivo se convierten al jugador que se movió (sin incluir el antiguo limo que se está moviendo).

    Por ejemplo, con el tablero en la Fig. 1, si el jugador 1 fuera a salir 0 1 1 2, el resultado sería el tablero en la Fig. 2.

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

También puede pasar, simplemente enviando coordenadas inválidas (ej. 0 0 0 0).

Reglas y restricciones

Las reglas adicionales son:

  • Puede leer y escribir archivos dentro de su propia carpeta para conservar los datos (los envíos se almacenarán players/YourBotName/yourBotName.language), pero no puede modificar ni acceder a nada fuera de ella. Se prohíbe el acceso a internet.
  • Es posible que su envío no esté codificado específicamente para ayudar o dañar otro envío. (Es posible que tenga varias presentaciones, pero no deben interactuar específicamente entre sí de ninguna manera).
  • Su envío no debe demorar más de 0.1 segundos por turno. Si su envío demora 0.105 segundos ocasionalmente, está bien, pero es posible que no tarde mucho más que este límite de tiempo. (Esto es principalmente un control de cordura para evitar que las pruebas tarden demasiado tiempo).
  • Su envío no debe ser un duplicado exacto (es decir, usar exactamente la misma lógica) de otro, incluso si está en un idioma diferente.
  • Su presentación debe ser una presentación seria. Esto se basa en la opinión, pero si su presentación claramente no intenta resolver el desafío (por ejemplo, si pasa cada turno), será descalificado.

Si su envío infringe alguna de estas reglas o no sigue la especificación, será descalificado, eliminado playerlist.txty el juego se reiniciará desde el principio. Si su presentación es descalificada, dejaré un comentario en su publicación explicando por qué. De lo contrario, su envío se agregará a la tabla de clasificación. (Si no ve su envío en la tabla de clasificación, no haga ningún comentario explicativo sobre su publicación y publique su envío antes del tiempo de "Última actualización" a continuación, ¡dígame! Es posible que lo haya pasado por alto inadvertidamente).

En su entrada, por favor incluya:

  • Un nombre.
  • Un comando shell para ejecutar el programa (por ejemplo, java MyBot.java, ruby MyBot.rb, python3 MyBot.py, etc.).
    • Tenga en cuenta que la entrada (su jugador y mapa) se agregará a esto como un argumento de línea de comando.
    • Los programas se probarán en Ubuntu 14.04, así que asegúrese de que su código se pueda ejecutar (libremente) en él.
  • Un número de versión, si su código funciona de manera diferente en diferentes versiones de su idioma.
  • El código de tu bot.
  • Instrucciones sobre cómo compilar el código, si es necesario.

Código / prueba del controlador, ejemplo bot

El código del controlador está escrito en C ++ y se puede encontrar en Github . Allí encontrará más instrucciones sobre cómo ejecutar y probar su código.

simplebot.rb, un bot muy simple que propaga o salta un limo aleatorio a una ubicación aleatoria cada turno, también se publica en Github .

Puntuación y tabla de clasificación

Cuando se llenan todas las casillas del tablero, el juego termina y se calculan las puntuaciones. La puntuación final de un jugador es la cantidad de cuadrados que contienen su baba al final del juego. Si han pasado 2000 turnos (500 para cada jugador) y el juego aún no ha terminado, el juego terminará de todos modos y los puntajes se informarán como si el juego hubiera terminado.

Al final del torneo, los puntajes de todos los juegos se promediarán para calcular el puntaje final de cada jugador, que se publicará en la tabla de clasificación. No hay fecha límite de presentación; Continuaré actualizando la tabla de clasificación periódicamente a medida que lleguen nuevos envíos.

Se requieren 4 presentaciones hasta que aparezca una tabla de clasificación real.

+--------------------------+-----------+--------------------+
| 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    |
+--------------------------+-----------+--------------------+

Última actualización: 22 de julio 10:27 p.m. (UTC).

Pomo de la puerta
fuente
Hmm, podría haberlo perdido, pero ¿explicaste cómo será la interacción entre jugadores? ¿Todos se mueven al mismo tiempo? Jugador 1 primero?
justhalf
1
Quizás solo soy yo quien encuentra esto un poco confuso, pero ¿cómo define exactamente "dos cuadrados de distancia"?
arshajii
Me recuerda mucho a un juego basado en una bebida de los noventa. ;-)
Benny
@justhalf El jugador 1 se mueve primero.
Pomo de la puerta
1
@arshajii "A dos cuadrados de distancia" significa, formalmente, "en cualquier posición donde el máximo del cambio en X y el cambio en Y sea igual a 2".
Pomo de la puerta

Respuestas:

4

GreedySlime

Simplemente realiza el movimiento que produce la mayor ganancia neta de unidades de limo.

Tenga en cuenta que esto está escrito en 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()

Ejecución de ejemplo (usando el ejemplo dado en la descripción del desafío, y suponiendo que el código se guarde en un archivo llamado slime.py):

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

Azul poco profundo

Shallow blue intenta descubrir qué podría suceder en el futuro haciendo una búsqueda exhaustiva en el árbol de posibles movimientos, desafortunadamente, no va más allá de su próximo turno. Luego golpea una puntuación media en cada estado del tablero posible después de su próximo turno, calcula una puntuación para cada rama individual con una fórmula igualmente ridícula y: ¡voila, se conoce el movimiento ideal!

EDITAR: El código original se ejecutó demasiado lento, así que lo modifiqué para que solo tome una muestra aleatoria de todos los movimientos posibles. Intentará casi todos los movimientos cuando haya pocos movimientos posibles y un porcentaje menor cuando haya más movimientos posibles.

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);
    }
}
overactor
fuente
No soy un gran programador y este enfoque fue mucho más complicado de lo que había anticipado, todavía no he probado este código (en absoluto) ya que son las 3 de la mañana y necesito trabajar temprano mañana. Es muy posible que mi bot se desconecte o simplemente no funcione. También podría haber equivocado las coordenadas, volveré a verlo todo mañana con ojos nuevos, pero un par adicional (o más) siempre son bienvenidos, un par adicional de ojos (más experimentados).
overactor
Estoy recibiendo un montón de excepciones para esto (ver chat para el stacktrace).
Pomo de la puerta
Mi código finalmente se está ejecutando, pero necesito desesperadamente aumentar la velocidad para poder disminuir el porcentaje de ramas que omito. ¿Alguien sabe cómo mejorar este desastre?
overactor
Dado que no está golfizado, podría publicarlo en la Revisión de Código ... ¿ Eso estaría dentro de las reglas o estaría mal visto?
trichoplax
1
Vi esta respuesta hace dos días, pero me di cuenta de que "Shallow Blue" es un juego de palabras para el famoso "Deep Blue".
justhalf
2

Saltador

Le gusta saltar, aún más hacia el medio.

Pasará si ningún limo puede saltar.

C ++ , debe compilar simplemente cong++ 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;
}

Robé tu verificación de movimiento lo siento. También renuncié a las prácticas correctas de codificación justo después de comenzar, así que no mire. Sin embargo, parece ejecutarse en cualquier tamaño de placa.

energía eólica
fuente
2

DeathSlime :

Descripción : Intenta cazar al enemigo más débil y destruirlo. Repetir.

Cómo ejecutar : ruby ​​DeathSlime.rb

Versió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
fingerco
fuente
1

Liquen

Este es un bot escrito en R. Debe activarse usando 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)

El algoritmo previsto es que intenta cubrir un área rectangular (llenando el espacio en blanco usando spread). Cuando el rectángulo está completo, semerges los dos limos en una de sus esquinas (el que está más alejado de la esquina del tablero) para expandir el área "habitable", luego llena el rectángulo recién definido, etc. No se usa 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.

Si un enemigo está en el área habitable y también hay un espacio vacío en el área, se llena el espacio vacío al lado. Si el limo que debería fusionarse al expandir el área habitable está rodeado de enemigos, entonces un limo se spreadfusionará en lugar de este.

plannapus
fuente
Estoy recibiendo un montón de errores para esto (ver chat para el stacktrace).
Pomo de la puerta
Bot ahora envía 0 0 0 0cuando no queda limo a bordo.
plannapus
0

CornerSlime

Este limo tiene una noción de esquinas, o al menos lo tenía cuando lo escribí por primera vez en C #, ya no estoy seguro.

Escrito en C ++, presumiblemente compilará bien con gcc y casi sin argumentos; ojalá no haya usado nada específico de MSVC por accidente.

Probado exclusivamente contra sí mismo en un servidor modificado (no hay un nuevo y sofisticado compilador de C ++ donde estoy), así que no tengo idea de cómo funcionará, con suerte no será descalificado por ser demasiado lento. Actualmente no hay aleatoriedad en este bot, pero puedo agregar algunos en una fecha posterior.

Este es C # portado a C ++ (debido a problemas de velocidad) por alguien que realmente no conoce C ++, y es horrendo. Comienza construyendo una matriz de Celdas, que luego llena con todo tipo de información inútil sobre las celdas a su alrededor (número de mis células, número de mis limos, ese tipo de cosas). Luego usa esta información para decidir si necesita mirar más de cerca la información que usó para crear esa información, y luego potencialmente usa dicha información para producir una salida significativa.

#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;
}
VisualMelon
fuente