¡Hagamos una guerra de tanques!
Parcialmente inspirado en destruirlos con lazers
Objetivo
Tu tarea es controlar un tanque. Muévete y dispara a otros tanques y obstáculos en el campo de batalla 2D. ¡El último tanque en pie será el ganador!
Formato de mapa
Su tanque estará en un campo 2D basado en una cuadrícula n
por n
cuadrícula de unidades. Decidiré qué n
se basa en el número de envíos. Cada cuadrado puede contener solo uno de:
- Un tanque
- Un árbol
- Una roca
- Una pared
- Nada
Todos los obstáculos y tanques llenan completamente sus espacios, y bloquean todos los disparos que los golpean para que no dañen las cosas más abajo.
Aquí hay un ejemplo de un campo con #
= tanque; T
= árbol; R
= roca; W
= pared; .
= nada con n
= 10
.....#....
..T....R..
WWW...WWWW
W......T..
T...R...Ww
W...W.....
W....W...T
WWWWWW...R
W.........
WWWWWWRT..
Las coordenadas están en el formato x, y
donde x
aumenta de izquierda a derecha y y
aumenta de abajo hacia arriba. El espacio inferior izquierdo tiene la coordenada 0, 0
. Cada tanque puede moverse a cualquier espacio vacío y disparar en cualquier dirección.
Dinámica del mapa
¡Tu tanque no solo tiene que disparar a otros tanques! Si dispara algo en el mapa, las cosas pueden suceder.
- Si se dispara a un muro, se destruirá después de cierto número de disparos, que van de 1 a 4
- Si le disparan a un árbol, será destruido inmediatamente
- Si se dispara una roca, la bala pasará sobre ella y dañará lo siguiente que golpee.
Una vez que se destruye algo, ya no está en el mapa (será reemplazado por nada). Si un disparo destruye un obstáculo, será bloqueado y no dañará nada más en su camino.
Dinámica del tanque
Cada tanque comienza con life
= 100. Cada disparo a un tanque reducirá 20-30 en life
función de la distancia. Esto se puede calcular con delta_life=-30+(shot_distance*10/diagonal_map_length)
(donde diagonal_map_length
está (n-1)*sqrt(2)
). Además, cada tanque se regenera 1 life
por turno.
Vueltas
Se ejecutará cierto número de rondas (lo decidiré una vez que tenga presentaciones). Al comienzo de cada ronda, se generará un mapa al azar y se colocarán tanques en ubicaciones vacías al azar. Durante cada ronda, cada tanque tendrá un turno, en cualquier orden arbitrario. Después de que cada tanque haya recibido un turno, se les dará turnos nuevamente en el mismo orden. La ronda continúa hasta que solo quede un tanque. Ese tanque será el ganador, y recibirán 1 punto. El juego pasará a la siguiente ronda.
Una vez que se hayan ejecutado todas las rondas, publicaré los puntajes en esta pregunta.
Durante el turno de un tanque, puede hacer uno de los siguientes
- Mueva hasta 3 espacios en una sola dirección, ya sea horizontal o verticalmente. Si el tanque está bloqueado por un obstáculo u otro tanque, se moverá lo más lejos posible sin pasar por el obstáculo o el tanque.
- Dispara en alguna dirección, representada por un ángulo de coma flotante en grados. El eje x del espacio local de su tanque (horizontalmente de izquierda a derecha, también conocido como este o
TurnAction.Direction.EAST
) es 0 grados, y los ángulos aumentan en sentido antihorario. Los disparos son inexactos, y el ángulo real del disparo puede ser 5 grados mayor o menor que el ángulo que elija. - Hacer nada.
Los turnos no están limitados en el tiempo, pero esto no significa que pueda perder tiempo intencionalmente para colgar todo.
Envíos / Protocolo
Cada programa presentado controlará un tanque en el campo. El programa de control está en Java, por lo que sus programas deben estar en Java por ahora (probablemente escribiré un contenedor para otros idiomas en algún momento, o podría escribir el suyo).
Sus programas implementarán la Tank
interfaz, que tiene los siguientes métodos:
public interface Tank {
// Called when the tank is placed on the battlefield.
public void onSpawn(Battlefield field, MapPoint position);
// Called to get an action for the tank on each turn.
public TurnAction onTurn(Battlefield field, MapPoint position, float health);
// Called with feedback after a turn is executed.
// newPosition and hit will be populated if applicable.
public void turnFeedback(MapPoint newPosition, FieldObjectType hit);
// Called when the tank is destroyed, either by another tank,
// or because the tank won. The won parameter indicates this.
public void onDestroyed(Battlefield field, boolean won);
// Return a unique name for your tank here.
public String getName();
}
La Battlefield
clase contiene una matriz 2D de objetos ( Battlefield.FIELD_SIZE
por Battlefield.FIELD_SIZE
) que representa cosas en el campo de batalla. Battlefield.getObjectTypeAt(...)
dará una FieldObjectType
para el objeto en las coordenadas especificadas (una de FieldObjectType.ROCK
, FieldObjectType.TREE
, FieldObjectType.TANK
, FieldObjectType.WALL
, o FieldObjectType.NOTHING
). Si intenta sacar un objeto fuera del alcance del mapa (coordenadas <0 o> = Battlefield.FIELD_SIZE
), se IllegalArgumentException
arrojará un .
MapPoint
es una clase para especificar puntos en el mapa. Use MapPoint.getX()
y MapPoint.getY()
para acceder a las coordenadas.
EDIT: se han añadido algunos métodos de utilidad: MapPoint.distanceTo(MapPoint)
, MapPoint.angleBetween(MapPoint)
, Battlefield.find(FieldObjectType)
, y TurnAction.createShootActionRadians(double)
según lo sugerido por Wasmoo .
Se puede encontrar más información en los javadocs, consulte la sección a continuación.
Todas las clases (API públicas) están bajo el paquete zove.ppcg.tankwar
.
Programa de control
La fuente completa y los javadocs del programa de control y la API del tanque se pueden encontrar en mi repositorio de GitHub: https://github.com/Hungary-Dude/TankWarControl
No dude en enviar solicitudes de extracción y / o comentarios si ve un error o desea una mejora.
He escrito dos programas de tanque de muestra RandomMoveTank
y RandomShootTank
(el nombre lo dice todo).
Para ejecutar su tanque, agregue su clase de tanque completamente calificada (nombre de paquete + nombre de clase) a tanks.list
(una clase por línea), edite la configuración según sea necesario en zove.ppcg.tankwar.Control
(demora de giro, si se muestra o no una representación GUI del campo, etc.), y ejecutar zove.ppcg.tankwar.Control
. Asegúrese de que haya al menos 2 tanques en la lista, o los resultados no están definidos. (Use los tanques de muestra si es necesario).
Sus programas se ejecutarán en mi máquina bajo este programa de control. Incluiré un enlace a la fuente una vez que lo escriba. No dude en sugerir ediciones a la fuente.
Reglas
- Sus envíos deben seguir las pautas anteriores
- Sus programas no pueden acceder al sistema de archivos, la red o intentar atacar mi máquina de ninguna manera
- Sus programas no pueden intentar explotar mi programa de control para hacer trampa
- Sin trolling (como hacer que su programa intencionalmente pierda tiempo para colgar todo)
- Puede tener más de una presentación
- ¡Intenta ser creativo con los envíos!
- Me reservo el derecho de permitir o no permitir programas arbitrariamente
¡Buena suerte!
ACTUALIZACIÓN: Después de corregir el error de teletransportación de la pared e implementar la regeneración, ejecuté los envíos actuales durante 100 rondas conBattlefield.FIELD_SIZE = 30
ACTUALIZACIÓN 2: agregué la nueva presentación, RunTank, después de engañar a Groovy por un momento ...
Resultados actualizados:
+-----------------+----+
| RandomMoveTank | 0 |
| RandomShootTank | 0 |
| Bouncing Tank | 4 |
| Richard-A Tank | 9 |
| Shoot Closest | 19 |
| HunterKiller 2 | 22 |
| RunTank | 23 |
| Dodge Tank | 24 |
+-----------------+----+
Actualmente los tanques regeneran 1 vida por turno. ¿Debería aumentarse eso?
fuente
MapPoint
'sx
, yy
floats
? ¿No deberían serloints
?Respuestas:
Cazador asesino
Este cazador inteligente intentará encontrar una posición segura donde pueda disparar limpiamente a exactamente un objetivo. (Y así, solo un objetivo puede dispararlo)
Funciona mejor cuando hay mucha cobertura.
Y eso es. Estoy gastado
fuente
Este tanque directo encuentra el tanque enemigo más cercano y le dispara. Sería bueno si
find
,distance
yangle
se integraran , y sicreateShootAction
aceptaran un doble en radianes (es decir, el resultado deangle
)Editar: Clase reescrita para incluir nuevos métodos de utilidad
fuente
Would be nice if find, distance, and angle were built in, and if createShootAction accepted a double in radians (i.e. the result of angle)
- Gran idea, loNo soy muy bueno en esto, pero pensé que aún le daría una oportunidad, ya sabes, practicar y esas cosas.
Mi tanque decidirá al azar moverse o disparar. Cuando decida disparar, intentará disparar al objetivo disponible más cercano.
El código completo que incluye el programa de control se puede encontrar aquí .
fuente
Direction.getRandom()
Dodge Tank
Este tanque disparará al tanque más cercano. De vez en cuando, dependiendo de su salud y de la última vez que se movió, intentará moverse perpendicular al tanque más cercano en un intento de esquivar sus láseres.
fuente
Esto fue mucho más complicado de lo que pensaba ...
Esta es mi entrada en Groovy, necesitas Groovy instalado y compilarlo con
Para llamarlo, debe agregar $ GROOVY_HOME / Groovy / Groovy-2.3.4 / lib / groovy-2.3.4.jar (o cualquier versión) al classpath.
Podría enviarle un archivo compilado .class y la biblioteca si no desea instalarlo.
Parece que hay una situación en la que los tanques no pueden verlo de otra manera, no sé si eso es lo que se pretende. Eso causó puntos muertos durante las pruebas.
De todos modos, aquí está RunTank: RunTank boldy avanza en la dirección opuesta del tanque más cercano si es el tanque más cercano al tanque más cercano o si hay más de un tanque dentro de FIELD_SIZE / 3. Espero que tenga sentido, estoy borracho :)
Tengo una sugerencia: agregue colores al tanque y un método para implementarlo. También las etiquetas estarían bien en la GUI :)
fuente
def RandomMoveTank() {}
- ¿Eso está destinado a estar allí? (No sé maravilloso)Esta es una variante del Shoot-Closest en que, cada dos turnos, se mueve en una dirección hasta que ya no puede. Dispara cada dos turnos.
Tiene una práctica utilidad,
path
que puede usarse para identificar todos los puntos (y, por lo tanto, los objetos) entre dos puntos.fuente