diff --git a/.env.template b/.env.template deleted file mode 100644 index 5f0721c..0000000 --- a/.env.template +++ /dev/null @@ -1,7 +0,0 @@ -LISTEN_ADDRESS=":50051" -JOIN_TIMEOUT="5s" -TURN_REQUEST_TIMEOUT="100ms" -TURNS="150" -BOARD_PATH="./maps/island_simple.txt" -VERBOSITY="true" -TIME_BETWEEN_ROUNDS="1s" \ No newline at end of file diff --git a/docker/Dockerfile.game b/Dockerfile similarity index 100% rename from docker/Dockerfile.game rename to Dockerfile diff --git a/README.md b/README.md index baf8913..dc8fb1b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,12 @@ the [Euskal Encounter](https://ee32.euskalencounter.org/) in Bilbao, Spain. We are proud to present the "Reloaded" version of the contest, which features a new engine from scratch written in Go and a proper GRPC API for the engine to communicate with the bots. -You can find the Game mechanics and rules at our [Game Documentation](https://coda.io/@gabri-igz/lighthouses). +- [Game Instructions](https://coda.io/@gabri-igz/lighthouses) +- [Game Engine Documentation](docs/game_engine.md) +- [Game Visualizer](https://intelygenz.github.io/codeconz-lighthouses-engine/) +- [Map Generator](https://dovixman.github.io/lighthouses_map_generator/) +- [Go bot template](https://github.com/intelygenz/codeconz-lighthouses-go-bot) +- [Python random bot template](https://github.com/intelygenz/codeconz-lighthouses-py-bot) To start building a bot, you will need to take the following steps: @@ -31,8 +36,9 @@ To start building a bot, you will need to take the following steps: Considerations: - Make sure the bot repository is owned by one of your team members. -- The project can be public or private. +- The repository can be public or private. - Only one repository is needed per bot. +- The name of the bot that will show at the [Game Visualizer](https://intelygenz.github.io/codeconz-lighthouses-engine/) will be `{username}-{repository}`. E.g. `intelygenz-codeconz-lighthouses-py-bot` ## Build an amazing bot! @@ -73,9 +79,11 @@ You can also run `./start-game.sh -h` to see the available options. ### Visualizing and diagnosing a game -To visualize and analyze the game, you can upload a JSON file from the `/output` folder to the [Lighthouses Game Visualizer](https://intelygenz.github.io/codeconz-lighthouses-engine/). +To visualize and analyze the game, you can upload a JSON file from the `/output` folder to the [Game Visualizer](https://intelygenz.github.io/codeconz-lighthouses-engine/). This tool will help you check what your bot did during the game and how it performed. +*Remember that the name of the bot in the [Game Visualizer](https://intelygenz.github.io/codeconz-lighthouses-engine/) will correspond with the name of the image `{username}/{repository}`* + ### Customizing the game If you are running the game with no changes at the `game.cfg` file, the game will run with the competition configuration, @@ -90,7 +98,9 @@ You will probably want to: To change the game configuration, you can edit the `game.cfg` file at the root of the engine repository: - `bots`: an array of bot images that will play the game: - `bots=('ghcr.io/john/bot-foo' 'docker.io/jane/bot-bar' ... 'quay.io/dave/bot-baz')` + ``` + bots=('ghcr.io/john/bot-foo' 'docker.io/jane/bot-bar' ... 'quay.io/dave/bot-baz') + ``` - `map`: the map file that will be used in the game: `map=square.txt` - `turns`: the number of turns the game will last: @@ -108,7 +118,7 @@ You are expected to create your own maps to test your bot under different scenar To do so: -- use the [Lighthouses Map Generator](https://dovixman.github.io/lighthouses_map_generator/) to generate a new map +- use the [Map Generator](https://dovixman.github.io/lighthouses_map_generator/) to generate a new map - copy the generated map to the `/maps` folder in the engine repository - update the `game.cfg` file to use the new map: `map={map_name}.txt` diff --git a/aicontest_spec.txt b/aicontest_spec.txt deleted file mode 100644 index 9d52bfb..0000000 --- a/aicontest_spec.txt +++ /dev/null @@ -1,449 +0,0 @@ -Euskal Encounter 21 - AI Contest -================================================================================ - FAROS LÁSER -================================================================================ - -v0.7 - -NOTA: esta especificación está sujeta a cambio. En particular, se reserva el -derecho de cambiar los valores numéricos mencionados, que no son definitivos. - -================================================================================ -El Mapa -================================================================================ - -El juego se jugará en un tablero de dos dimensiones compuesto por una matriz de -casillas. Cada partida transcurrirá sobre un mapa de forma y dimensiones fijadas -al comienzo de ella. El mapa define qué subconjunto de las casillas forma el -área jugable (la isla). En todos los casos las casillas del borde del mapa no -formarán parte del área jugable (lo cual excluye la posibilidad de mapas con -"wraparound"). Además, todas las casillas de la "isla" estarán conectadas entre -sí (no son válidos los mapas con áreas disjuntas). - -Ejemplo de un mapa válido: - -XXXXXXXXXXXX -XXXX X -X XX X -XX X XX -XX XXXX XX -XX XX -XXXXXXXXXXXX - -Se considera que la casilla inferior izquierda es la casilla (0,0) y las -coordenadas crecen hacia arriba y a la derecha. - -================================================================================ -Energía -================================================================================ - -La energía es indispensable para jugar el juego. Cada casilla de la isla tendrá -una cantidad de energía disponible, inicialmente 0, que se irá incrementando -durante la partida. Los jugadores que pisan una casilla obtienen la energía -que hay en ella, que pasa de nuevo a 0. La energía siempre es un entero (no hay -unidades fraccionarias). - -================================================================================ -Los jugadores -================================================================================ - -Cada partida la jugarán dos o más jugadores identificados por los números 0, 1, -2, etc. - -Cada jugador tiene una posición actual en el mapa. La posición al comienzo de la -partida estará definida en el mapa. - -Los jugadores podrán viajar por la isla de forma libre, en cualquiera de las -ocho direcciones (horizontal, vertical, y diagonal). Varios jugadores pueden -ocupar la misma casilla en el mismo momento. No se podrá transcurrir por las -casillas que no forman parte de la isla. - -Los jugadores dispondrán de una reserva de energía (0 al comienzo de la -partida). La energía se obtendrá de las casillas. En principio no habrá límite -sobre la energía de cada jugador, aunque se reserva el derecho a establecer un -máximo. - -================================================================================ -Los faros -================================================================================ - -En el mapa se situarán una serie de faros. Cada faro puede ser neutro o estar -controlado por un jugador. Cuando un faro está controlado por un jugador, tendrá -una cantidad de energía asociada a el. Esta energía disminuirá con el tiempo, -y cuando llega a 0 el faro pasará a ser neutro de nuevo. No hay un máximo de -energía por faro. - -Los faros generan energía de forma continua en las casillas cercanas a -ellos. Esta energía no proviene del faro en sí, es decir, es independiente de -si el faro está controlado por un jugador, o si es neutro, y de su nivel actual -de energía. - -================================================================================ -Conexiones -================================================================================ - -Los jugadores podrán conectar parejas de faros mediante haces láser cuando ambos -están bajo su control. Sin embargo, está prohibido lanzar un haz entre dos faros -que cruce un haz ya existente (tanto del mismo jugador como del enemigo). Si se -hiciera, la densidad de flujo lumínico en la intersección sería tan alta que se -iniciaría una fusión no controlada de la atmósfera y por lo tanto una catástrofe -global. También está prohibido conectar dos faros si la conexión pasaría -exactamente por un tercer faro. - -Para realizar una conexión entre dos faros se debe estar en la casilla de uno -de ellos y tener la clave del otro faro. Para obtener la clave de un faro se -deberá visitarlo, y sólo se puede tener una clave en cada momento, que se -perderá al realizar una conexión (las claves son de un sólo uso, OTP). - -Cuando tres faros se conectan diréctamente entre sí formando un triángulo, se -iluminan todas las casillas situadas en el interior de dicho triángulo. El -objetivo del juego es tener el máximo número de casillas iluminadas por -áreas triangulares entre faros bajo el control del mismo jugador. - -Las conexiones pueden cruzar casillas que no forman parte de la isla. Las -casillas que no forman parte de la isla no se iluminan ni puntúan, pero pueden -formar parte de un triángulo. - -================================================================================ -El Juego -================================================================================ - -La partida constará de una serie de rondas. Durante cada ronda primero será el -turno del jugador 0, después el 1, etc. - -Al comienzo del juego, cada jugador recibirá la siguiente información: -- Su número de jugador -- El número de jugadores total -- Su posición inicial -- El mapa (es decir un mapa booleano de qué casillas forman parte de la isla) -- Por cada faro: - - Sus coordenadas - -Al comienzo de cada ronda ocurrirá lo siguiente: -- Cada casilla incrementará su cantidad de energía libre disponible según la - siguiente fórmula: - energía += floor(5 - distancia a faro) - Esto ocurre por cada faro, de forma que las casillas a menos de 5 unidades - de distancia lineal a más de un faro se verán afectadas por todos ellos. - Cada casilla se limitará a un máximo de 100 unidades de energía. -- Cada jugador obtendrá de su casilla actual la cantidad de energía libre - presente en ella, que se reducirá a cero. Si varios jugadores comparten - casilla, cada uno recibirá la fracción correspondiente (si hay resto tras la - división, la energía restante se pierde). -- Si un jugador está situado sobre un faro, obtendrá su clave si no dispone ya - de ella. Esto ocurre indistintamente de si el faro está controlado por el o - no. -- La energía de todos los faros se decrementa en 10 puntos. Si llega a cero, - el faro pasa a ser neutro y desaparecen todas las conexiones con él. - -Antes de cada turno, el jugador recibe la siguiente información: - -- Su posición -- Su puntuación -- Su nivel de energía acumulada -- Por cada casilla cuyo centro se haya a 3 unidades o menos del jugador (en - línea recta): - - La cantidad de energía libre disponible en ella. Lógicamente, el valor - siempre será cero para la casilla del propio jugador, ya que esa energía ya - ya sido obtenida. -- Por cada faro: - - Sus coordenadas (como identificador) - - El jugador que lo controla (0, 1, 2, ... o ninguno=-1) - - Su nivel de energía (cero si no está bajo control) - - La lista de faros a los cuales está conectado (por lo tanto cada conexión se - indicará por duplicado, desde ambos vértices). - - Si se dispone de su clave o no. - -Cabe resaltar que los jugadores NO conocen la posición de los demás. - -Cada turno, cada jugador podrá realizar UNA de las siguientes acciones: - -- Nada -- Mover a una casilla adyacente -- Si se encuentra en la misma casilla que un faro: - - Atacar o recargarlo. Atacar o recargar un faro es la misma acción: el - jugador aporta una cantidad de energía de su elección. Si el faro no está - bajo su control, se resta total o parcialmente de la energía presente en el - faro. Cuando toda la energía del faro ha sido eliminada, el sobrante de - energía aportada se suma de nuevo al faro y el faro pasa a estar controlado - por el jugador actual. - - Conectarlo a otro faro bajo el control del mismo jugador. Para ello, el - jugador debe disponer de la clave del faro remoto, y la perderá al crear - la conexión. - - -================================================================================ -Puntuación -================================================================================ - -Cada jugador tendrá una puntuación (0 al comienzo de la partida), que es un -número entero. Se obtendrán puntos al término de cada ronda de la siguiente -forma: - -- 2 puntos por cada torre bajo control del jugador. -- 2 puntos por cada pareja de faros conectados bajo el control del jugador. -- Por cada 3 faros conectados entre sí bajo su control (formando un triángulo): - - 1 punto por cada casilla cuyo centro esté dentro de dicho triángulo. - - Nota 1: es posible que las áreas se solapen, y, en ese caso, puntúan doble. - Por ejemplo, en esta configuración de faros conectados existen 3 triángulos - interiores y un triángulo exterior, de forma que la cantidad de puntos - otorgados será equivalente al DOBLE del área del triángulo exterior. - - o-----o - \`o'/ - \|/ - o - - Igualmente, es posible que un área de un jugador contenga completamente a - un área de otro jugador. En este caso cada jugador obtiene sus puntos - correspondientes. Es decir, la casilla se puede "iluminar" de una mezcla de - colores. - - Nota 2: Cuando un lado del triángulo pasa por el centro de una casilla, se - utilizará la regla de rasterización de OpenGL para determinar qué casillas - se consideran como parte del área (cuentan las casillas superiores y a la - izquierda): http://goo.gl/f1cxU - -================================================================================ -Fin del juego -================================================================================ - -El juego terminará tras un número predefinido de rondas. - -================================================================================ -Protocolo -================================================================================ - -Los bots se comunicarán por stdio/stdout. El protocolo consiste en mensajes -JSON codificados en una sola línea, terminados por \n (newline). El motor del -juego siempre es el responsable de iniciar las comunicaciones, y el bot -contestará con su respuesta. Los bots pueden mostrar mensajes de debug por -stderr, pero DEBEN prefijarlos por su nombre entre [] para su clara -identificación: - -[TroloBot] mensaje... - -Nota: los siguiente ejemplos están formateados en varias líneas, pero en -el protocolo real deberán estar contenidos exclusivamente en una línea. - ----------------- -Inicio del juego ----------------- -El motor envía el siguiente mensaje (ejemplo): -{ - "player_num": 0, - "player_count": 2, - "position": [1, 2], - "map": [ - [0, 0, 0, 0, 0], - [0, 1, 1, 1, 0], - [0, 1, 1, 0, 0], - [0, 1, 1, 0, 0], - [0, 0, 0, 0, 0]], - "lighthouses": [ - [1, 1], [3, 1], [2, 3], [1, 3] - ] -} - -Este mensaje indica que el bot es el primer jugador (jugador 0) de 2 en total -(el otro sería el jugador 1), y describe el siguiente mapa de 5x5: - -v----- casilla (0, 4) -XXXXX<-- casilla (4,4) -X!!XX -X0 XX -X! !X -XXXXX<-- casilla (4,0) -^----- casilla (0,0) - -! - faro -0 - posición inicial del jugador 0 - -Nótese que el mapa se envía de abajo hacia arriba (0,0 es la primera casilla -que se envía, pero es la esquina inferior izquierda). En la práctica la -dirección en la que se visualice el mapa es inconsecuente para el juego, -excepto en lo que respecta a la iluminación de casillas, que sigue la regla -de bordes izquierdo y superior asumiendo que (0,0) es la esquina inferior -izquierda. - -El bot contesta con el siguiente mensaje: -{ - "name": "TroloBot" -} - -El nombre se utilizará para mostrar el nombre del bot en pantalla. -El bot debe inicializarse y contestar en un máximo de 2 segundos tras el -envío del mensaje de inicio. - ----------------- -Turno ----------------- -El motor envía el siguiente mensaje con el estado actual (ejemplo): -{ - "position": [1, 3], - "score": 36, - "energy": 66, - "view": [ - [-1,-1,-1, 0,-1,-1,-1], - [-1, 0, 0,50,23,50,-1], - [-1, 0, 0,32,41, 0,-1], - [ 0, 0, 0, 0,50, 0, 0], - [-1, 0, 0, 0, 0, 0,-1], - [-1, 0, 0, 0, 0, 0,-1], - [-1,-1,-1, 0,-1,-1,-1] - ], - "lighthouses": [ - { - "position": [1, 1], - "owner": 0, - "energy": 30, - "connections": [[1, 3]], - "have_key": false - }, - { - "position": [3, 1], - "owner": -1, - "energy": 0, - "connections": [], - "have_key": false - }, - { - "position": [2, 3], - "owner": 1, - "energy": 90, - "connections": [], - "have_key": false - }, - { - "position": [1, 3], - "owner": 0, - "energy": 50, - "connections": [[1, 1]], - "have_key": true - } - ] -} - -"view" es un mapa de la energía disponible en las celdas cercanas al jugador. -Nominalmente es de 7x7, conteniendo las casillas a distancia 3 o menos del -jugador. Los bots deben tolerar que este valor varíe, pero siempre se dará -el caso de que "view" es una matriz cuadrada de dimensión impar, y la posición -del bot corresponde a la casilla del medio de la matriz. Las celdas fuera del -radio 3 se devuelven como -1 para indicar que no hay datos. Las celdas que -no forman parte de la isla se devuelven como 0 ya que nunca pueden contener -energía (las casillas que sí forman parte de la isla pero no tienen energía -también se devuelven como 0). Ya que el jugador obtiene la energía de la -casilla central al comienzo de cada ronda, esta siempre tendrá un valor de 0. - -En este caso, la vista nos proporciona la siguiente información sobre el mapa: - -X X X X ?? -X () 50 X X -X 32 41 X ?? -X 50 23 50 ?? -?? X ?? ?? ?? - -() - posición jugador 0 (energía 0) -?? - no hay datos (en este mapa, por ser pequeño, resulta que todas estas -casillas no forman parte de la isla; este no sería el caso en mapas mayores) -X - casilla no forma parte de la isla, pero entra dentro del área visible - (distancia 3 o menor). Este hecho viene del mapa del mensaje inicial. - -El mensaje describe los faros siguientes: - -X X X X X -X 50 90 X X -X | X X -X 30 0 X -X X X X X - -Donde cada número indica la energía del faro. Los faros con energía == 50, 30 -están controlados por el jugador 0 y conectados entre sí, y el faro con -energía == 90 está controlado por el jugador 1. Bajo estas condiciones, el -jugador 0 obtiene 6 puntos por ronda, y el jugador 1 obtiene 2 por ronda. -Además, el jugador 0 tiene la clave del faro con energía 50 (es de suponer que -visitó el faro 30 para obtener su clave, y luego el 50 para conectarlo al -30, perdiendo la clave del 30 en el proceso, pero ahora dispone de la clave del -50 por estar situado sobre el). - -El bot debe contestar con uno de los siguientes mensajes (tiempo máximo por -turno: 100ms desde que se recibe el estado hasta que se contesta): -{ - "command": "pass" -} -No hacer nada (pasar el turno) - -{ - "command": "move", - "x": -1, - "y": 1 -} -Mover a una casilla adyacente (en este caso, arriba y hacia la izquierda). -Obviamente x e y deben ser -1, 0, o 1. Mover 0,0 es equivalente a pasar el -turno. - -{ - "command": "attack", - "energy": 80 -} -Atacar o recargar el faro sobre el cual se encuentra el jugador. Por ejemplo, -si el faro está controlado por otro jugador con energía 50, entonces pasaría a -estar controlado por el jugador actual con energía 30. Si el faro está -controlado por otro jugador con energía 90, entonces seguiría controlado por el -mismo jugador con energía 10. Si el faro está controlado por el jugador actual -con energía 40, entonces pasa a tener energía 120. Si el faro está controlado -por otro jugador con energía 80, entonces pasa a ser neutro (no controlado por -ningún jugador) con energía 0. Al perderse o cambiar el control de un faro, -se destruyen todas las conexiones actuales y por extensión dejan de existir -todas las áreas (triángulos) que tienen ese faro como vértice (aunque es posible -que un área que contenga íntegramente al faro no se vea afectada). La energía -suministrada debe ser igual o menor a la energía que posee el jugador -actualmente (si no lo fuera, se limita automáticamente al total disponible). -Atacar con energía 0 es equivalente a pasar el turno. - -{ - "command": "connect", - "destination": [1, 1] -} -Conectar el faro de la posición actual al faro (1, 1). El jugador debe poseer -la clave del faro de destino, y ambos deben estar controlados por el jugador. -La conexión no debe existir ya, ni cruzarse contra ninguna otra conexión -existente, ni pasar por el centro de un tercer faro (la conexión se puede -solapar con la casilla de un tercer faro siempre y cuando no lo haga por su -centro exacto). - -El motor del juego contesta con el resultado de la operación: -{ - "success": true -} -O bien (ejemplo de error): -{ - "success": false, - "message": "Player does not have the destination key" -} -En caso de error, el resultado es el mismo que si el jugador hubiera pasado el -turno. - ----------------- -Fin del juego ----------------- -Al término del juego, el motor simplemente cierra stdin y stdout. El bot debe -cerrarse correctamente al detectar una condición de EOF en stdin. - - -En resumen, las comunicaciones con el bot siguien la siguiente progresión: -<< Inicialización (mapa, faros, etc.) ->> Hello (nombre del bot) -<< Estado al comienzo del turno (faros, conexiones, energía, etc.) ->> Jugada (comando) -<< Resultado de la jugada -<< Estado al comienzo del turno (faros, conexiones, energía, etc.) ->> Jugada (comando) -<< Resultado de la jugada -(...) -<< Estado al comienzo del turno (faros, conexiones, energía, etc.) ->> Jugada (comando) -<< Resultado de la jugada -<< [EOF] ->> [EOF] diff --git a/docker/Dockerfile.gobot b/docker/Dockerfile.gobot deleted file mode 100644 index af2a086..0000000 --- a/docker/Dockerfile.gobot +++ /dev/null @@ -1,22 +0,0 @@ -# Build the Go binary -FROM golang:1.22-alpine AS builder -ARG BOT_NAME -ARG GAME_PORT=50051 -WORKDIR /app -COPY . ./ -RUN go mod download && \ - CGO_ENABLED=0 GOOS=linux go build -o ${BOT_NAME} ./examples/ranbot.go - -FROM alpine:3.20.3 -ARG BOT_NAME -ARG BOT_PORT -WORKDIR /app -COPY ./proto/ ./proto/ -COPY --from=builder /app/${BOT_NAME} ./ -EXPOSE ${GAME_PORT} -RUN adduser -h /app -H -s /sbin/nologin -D -u 10000 botuser && \ - chown -R botuser:botuser /app -USER botuser -EXPOSE ${BOT_PORT} -# Command to run the Go binary -CMD ./${BOT_NAME} -bn=${BOT_NAME} -la=${BOT_NAME}:${BOT_PORT} -gs=game:${GAME_PORT} diff --git a/docker/Dockerfile.pybot b/docker/Dockerfile.pybot deleted file mode 100644 index 8e75ae9..0000000 --- a/docker/Dockerfile.pybot +++ /dev/null @@ -1,15 +0,0 @@ -FROM python:3.10-slim - -ARG BOT_NAME -ARG GAME_PORT=50051 -ARG BOT_PORT - -WORKDIR /app -COPY ./ ./ -COPY ./examples/randbot.py ./main.py - -RUN pip install --no-cache-dir -r requirements.txt - -EXPOSE ${BOT_PORT} - -CMD python3 ./main.py --bn=${BOT_NAME} --la=${BOT_NAME}:${BOT_PORT} --gs=game:${GAME_PORT} diff --git a/examples/ranbot.go b/examples/ranbot.go deleted file mode 100644 index ea90fef..0000000 --- a/examples/ranbot.go +++ /dev/null @@ -1,307 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "net" - "time" - - "github.com/jonasdacruz/lighthouses_aicontest/internal/handler/coms" - "github.com/spf13/viper" - "google.golang.org/grpc/status" - - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -const ( - timeoutToResponse = 1 * time.Second -) - -var countT = 1 - -type BotGameTurn struct { - turn *coms.NewTurn - action *coms.NewAction -} - -type BotGame struct { - initialState *coms.NewPlayerInitialState - turnStates []BotGameTurn -} - -func (bg *BotGame) NewTurnAction(turn *coms.NewTurn) *coms.NewAction { - action := &coms.NewAction{ - Action: coms.Action_MOVE, - Destination: &coms.Position{ - X: turn.Position.X + 1, - Y: turn.Position.Y, - }, - } - - if countT == 2 { - action = &coms.NewAction{ - Action: coms.Action_MOVE, - Destination: &coms.Position{ - X: turn.Position.X, - Y: turn.Position.Y + 1, - }, - } - } - - if countT == 3 || countT == 6 || countT == 10 || countT == 14 { - action = &coms.NewAction{ - Action: coms.Action_ATTACK, - Energy: turn.Energy, - Destination: &coms.Position{ - X: turn.Position.X, - Y: turn.Position.Y, - }, - } - } - - if countT == 4 || countT == 5 { - action = &coms.NewAction{ - Action: coms.Action_MOVE, - Destination: &coms.Position{ - X: turn.Position.X - 1, - Y: turn.Position.Y, - }, - } - } - - if countT == 7 { - action = &coms.NewAction{ - Action: coms.Action_CONNECT, - Destination: &coms.Position{ - X: 3, - Y: 3, - }, - } - } - - if countT == 8 || countT == 9 { - action = &coms.NewAction{ - Action: coms.Action_MOVE, - Destination: &coms.Position{ - X: turn.Position.X, - Y: turn.Position.Y - 1, - }, - } - } - - if countT == 11 { - action = &coms.NewAction{ - Action: coms.Action_CONNECT, - Destination: &coms.Position{ - X: 1, - Y: 3, - }, - } - } - - if countT == 12 || countT == 13 { - action = &coms.NewAction{ - Action: coms.Action_MOVE, - Destination: &coms.Position{ - X: turn.Position.X + 1, - Y: turn.Position.Y + 1, - }, - } - } - - if countT == 15 { - action = &coms.NewAction{ - Action: coms.Action_CONNECT, - Destination: &coms.Position{ - X: 1, - Y: 1, - }, - } - } - - bgt := BotGameTurn{ - turn: turn, - action: action, - } - bg.turnStates = append(bg.turnStates, bgt) - - countT += 1 - return action -} - -type BotComs struct { - botID int - botName string - myAddress, gameServerAddress string -} - -func (ps *BotComs) waitToJoinGame() { - grpcOpt := grpc.WithTransportCredentials(insecure.NewCredentials()) - grpcClient, err := grpc.NewClient(ps.gameServerAddress, grpcOpt) - if err != nil { - fmt.Printf("grpc client ERROR: %v\n", err) - panic("could not create a grpc client") - } - - npjc := coms.NewGameServiceClient(grpcClient) - - player := &coms.NewPlayer{ - Name: ps.botName, - ServerAddress: ps.myAddress, - } - - for { - ctx, cancel := context.WithTimeout(context.Background(), timeoutToResponse) - playerID, err := npjc.Join(ctx, player) - - if err != nil { - fmt.Printf("could not join game ERROR: %v\n", err) - cancel() - continue - } else { - fmt.Printf("Joined game with ID %d\n", int(playerID.PlayerID)) - ps.botID = int(playerID.PlayerID) - - if viper.GetBool("bot.verbosity") { - b, err := json.Marshal(playerID) - if err != nil { - fmt.Println(err) - return - } - fmt.Println(string(b)) - } - break - } - } -} - -func (ps *BotComs) startListening() { - fmt.Println("Starting to listen on", ps.myAddress) - - lis, err := net.Listen("tcp", ps.myAddress) - if err != nil { - panic(err) - } - - grpcServer := grpc.NewServer( - grpc.UnaryInterceptor(UnaryLoggingInterceptor), - grpc.StreamInterceptor(StreamLoggingInterceptor), - ) - cs := &ClientServer{} - coms.RegisterGameServiceServer(grpcServer, cs) - - if err := grpcServer.Serve(lis); err != nil { - panic(err) - } -} - -func UnaryLoggingInterceptor( - ctx context.Context, - req interface{}, - info *grpc.UnaryServerInfo, - handler grpc.UnaryHandler, -) (resp interface{}, err error) { - start := time.Now() - resp, err = handler(ctx, req) - duration := time.Since(start) - st, _ := status.FromError(err) - fmt.Printf("unary call: %s, Duration: %v, Error: %v\n", info.FullMethod, duration, st.Message()) - return resp, err -} - -func StreamLoggingInterceptor( - srv interface{}, - ss grpc.ServerStream, - info *grpc.StreamServerInfo, - handler grpc.StreamHandler, -) error { - start := time.Now() - err := handler(srv, ss) - duration := time.Since(start) - st, _ := status.FromError(err) - fmt.Printf("stream call: %s, Duration: %v, Error: %v\n", info.FullMethod, duration, st.Message()) - return err -} - -type ClientServer struct { - bg *BotGame -} - -func (gs *ClientServer) Join(_ context.Context, _ *coms.NewPlayer) (*coms.PlayerID, error) { - return nil, fmt.Errorf("random bot does not implement Join service") -} - -func (gs *ClientServer) InitialState(_ context.Context, initialState *coms.NewPlayerInitialState) (*coms.PlayerReady, error) { - fmt.Println("random bot receiving InitialState") - - gs.bg = &BotGame{} - gs.bg.initialState = initialState - - if viper.GetBool("bot.verbosity") { - b, err := json.Marshal(initialState) - if err != nil { - return nil, err - } - fmt.Println(string(b)) - } - - resp := coms.PlayerReady{Ready: true} - return &resp, nil -} - -func (gs *ClientServer) Turn(_ context.Context, turn *coms.NewTurn) (*coms.NewAction, error) { - if viper.GetBool("bot.verbosity") { - b, err := json.Marshal(turn) - if err != nil { - fmt.Println(err) - return nil, err - } - fmt.Println(string(b)) - } - - action := gs.bg.NewTurnAction(turn) - - return action, nil -} - -func ensureParams() (botName *string, listenAddress *string, gameServerAddress *string) { - botName = flag.String("bn", "random-bot", "bot name") - listenAddress = flag.String("la", "", "my listen address") - gameServerAddress = flag.String("gs", "", "game server address") - flag.Parse() - - if *botName == "" { - panic("bot name is required") - } - if *listenAddress == "" { - panic("listen address is required") - } - if *gameServerAddress == "" { - panic("game server address is required") - } - return botName, listenAddress, gameServerAddress -} - -func main() { - // init configuration - viper.SetDefault("bot.verbosity", true) - - botName, listenAddress, gameServerAddress := ensureParams() - - bot := &BotComs{ - botName: *botName, - myAddress: *listenAddress, - gameServerAddress: *gameServerAddress, - } - - bot.waitToJoinGame() - - // TODO: may be it needs to be 1 more step to retrieve initial state and process it - // bot.getInitialState() - - bot.startListening() - -} diff --git a/examples/randbot.py b/examples/randbot.py deleted file mode 100644 index a15a735..0000000 --- a/examples/randbot.py +++ /dev/null @@ -1,219 +0,0 @@ -import time -import argparse -import grpc -import random -from concurrent import futures -from grpc import RpcError -from google.protobuf import json_format - -from internal.handler.coms import game_pb2 -from internal.handler.coms import game_pb2_grpc as game_grpc - -timeout_to_response = 1 # 1 second - -class BotGameTurn: - def __init__(self, turn, action): - self.turn = turn - self.action = action - - -class BotGame: - def __init__(self, player_num=None): - self.player_num = player_num - self.initial_state = None - self.turn_states = [] - self.countT = 1 - - def new_turn_action(self, turn: game_pb2.NewTurn) -> game_pb2.NewAction: - cx, cy = turn.Position.X, turn.Position.Y - - lighthouses = dict() - for lh in turn.Lighthouses: - lighthouses[(lh.Position.X, lh.Position.Y)] = lh - - # Si estamos en un faro... - if (cx, cy) in lighthouses: - # Probabilidad 60%: conectar con faro remoto válido - if lighthouses[(cx, cy)].Owner == self.player_num: - if random.randrange(100) < 60: - possible_connections = [] - for dest in lighthouses: - # No conectar con sigo mismo - # No conectar si no tenemos la clave - # No conectar si ya existe la conexión - # No conectar si no controlamos el destino - # Nota: no comprobamos si la conexión se cruza. - if (dest != (cx, cy) and - lighthouses[dest].HaveKey and - [cx, cy] not in lighthouses[dest].Connections and - lighthouses[dest].Owner == self.player_num): - possible_connections.append(dest) - - if possible_connections: - possible_connection = random.choice(possible_connections) - action = game_pb2.NewAction( - Action=game_pb2.CONNECT, - Destination=game_pb2.Position(X=possible_connection[0], Y=possible_connection[1]) - ) - bgt = BotGameTurn(turn, action) - self.turn_states.append(bgt) - - self.countT += 1 - return action - - # Probabilidad 60%: recargar el faro - if random.randrange(100) < 60: - energy = random.randrange(turn.Energy + 1) - action = game_pb2.NewAction( - Action=game_pb2.ATTACK, - Energy=energy, - Destination=game_pb2.Position( - X=turn.Position.X, - Y=turn.Position.Y - ) - ) - bgt = BotGameTurn(turn, action) - self.turn_states.append(bgt) - - self.countT += 1 - return action - - # Mover aleatoriamente - moves = ((-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)) - move = random.choice(moves) - action = game_pb2.NewAction( - Action=game_pb2.MOVE, - Destination=game_pb2.Position( - X=turn.Position.X + move[0], - Y=turn.Position.Y + move[1] - ) - ) - - bgt = BotGameTurn(turn, action) - self.turn_states.append(bgt) - - self.countT += 1 - return action - - -class BotComs: - def __init__(self, bot_name, my_address, game_server_address, verbose=False): - self.bot_id = None - self.bot_name = bot_name - self.my_address = my_address - self.game_server_address = game_server_address - self.verbose = verbose - - def wait_to_join_game(self): - channel = grpc.insecure_channel(self.game_server_address) - client = game_grpc.GameServiceStub(channel) - - player = game_pb2.NewPlayer(name=self.bot_name, serverAddress=self.my_address) - - while True: - try: - player_id = client.Join(player, timeout=timeout_to_response) - self.bot_id = player_id.PlayerID - print(f"Joined game with ID {player_id.PlayerID}") - if self.verbose: - print(json_format.MessageToJson(player_id)) - break - except RpcError as e: - print(f"Could not join game: {e.details()}") - time.sleep(1) - - - def start_listening(self): - print("Starting to listen on", self.my_address) - - # configure gRPC server - grpc_server = grpc.server( - futures.ThreadPoolExecutor(max_workers=10), - interceptors=(ServerInterceptor(),) - ) - - # registry of the service - cs = ClientServer(bot_id=self.bot_id, verbose=self.verbose) - game_grpc.add_GameServiceServicer_to_server(cs, grpc_server) - - # server start - grpc_server.add_insecure_port(self.my_address) - grpc_server.start() - - try: - grpc_server.wait_for_termination() # wait until server finish - except KeyboardInterrupt: - grpc_server.stop(0) - - -class ServerInterceptor(grpc.ServerInterceptor): - def intercept_service(self, continuation, handler_call_details): - start_time = time.time_ns() - method_name = handler_call_details.method - - # Invoke the actual RPC - response = continuation(handler_call_details) - - # Log after the call - duration = time.time_ns() - start_time - print(f"Unary call: {method_name}, Duration: {duration:.2f} nanoseconds") - return response - - -class ClientServer(game_grpc.GameServiceServicer): - def __init__(self, bot_id, verbose=False): - self.bg = BotGame(bot_id) - self.verbose = verbose - - def Join(self, request, context): - return None - - def InitialState(self, request, context): - print("Receiving InitialState") - if self.verbose: - print(json_format.MessageToJson(request)) - self.bg.initial_state = request - return game_pb2.PlayerReady(Ready=True) - - def Turn(self, request, context): - print(f"Processing turn: {self.bg.countT}") - if self.verbose: - print(json_format.MessageToJson(request)) - action = self.bg.new_turn_action(request) - return action - - -def ensure_params(): - parser = argparse.ArgumentParser(description="Bot configuration") - parser.add_argument("--bn", type=str, default="random-bot", help="Bot name") - parser.add_argument("--la", type=str, required=True, help="Listen address") - parser.add_argument("--gs", type=str, required=True, help="Game server address") - - args = parser.parse_args() - - if not args.bn: - raise ValueError("Bot name is required") - if not args.la: - raise ValueError("Listen address is required") - if not args.gs: - raise ValueError("Game server address is required") - - return args.bn, args.la, args.gs - - -def main(): - verbose = False - bot_name, listen_address, game_server_address = ensure_params() - - bot = BotComs( - bot_name=bot_name, - my_address=listen_address, - game_server_address=game_server_address, - verbose=verbose, - ) - bot.wait_to_join_game() - bot.start_listening() - - -if __name__ == "__main__": - main() diff --git a/index.html b/index.html deleted file mode 100644 index c9f42d2..0000000 --- a/index.html +++ /dev/null @@ -1 +0,0 @@ -Hello Igz! diff --git a/internal/engine/game/initialstate.go b/internal/engine/game/initialstate.go index ad81997..bfd7ad7 100644 --- a/internal/engine/game/initialstate.go +++ b/internal/engine/game/initialstate.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "time" "github.com/jonasdacruz/lighthouses_aicontest/internal/engine/lighthouse" "github.com/jonasdacruz/lighthouses_aicontest/internal/engine/player" @@ -57,7 +58,8 @@ func (e *Game) SendInitialState() { panic(err) } - ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("turn_request_timeout")) + // Add 5 seconds timeout to the InitialState call + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // sending initial state to player diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 27ac2c5..0000000 --- a/poetry.lock +++ /dev/null @@ -1,262 +0,0 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. - -[[package]] -name = "grpcio" -version = "1.66.2" -description = "HTTP/2-based RPC framework" -optional = false -python-versions = ">=3.8" -files = [ - {file = "grpcio-1.66.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:fe96281713168a3270878255983d2cb1a97e034325c8c2c25169a69289d3ecfa"}, - {file = "grpcio-1.66.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:73fc8f8b9b5c4a03e802b3cd0c18b2b06b410d3c1dcbef989fdeb943bd44aff7"}, - {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:03b0b307ba26fae695e067b94cbb014e27390f8bc5ac7a3a39b7723fed085604"}, - {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d69ce1f324dc2d71e40c9261d3fdbe7d4c9d60f332069ff9b2a4d8a257c7b2b"}, - {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05bc2ceadc2529ab0b227b1310d249d95d9001cd106aa4d31e8871ad3c428d73"}, - {file = "grpcio-1.66.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ac475e8da31484efa25abb774674d837b343afb78bb3bcdef10f81a93e3d6bf"}, - {file = "grpcio-1.66.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0be4e0490c28da5377283861bed2941d1d20ec017ca397a5df4394d1c31a9b50"}, - {file = "grpcio-1.66.2-cp310-cp310-win32.whl", hash = "sha256:4e504572433f4e72b12394977679161d495c4c9581ba34a88d843eaf0f2fbd39"}, - {file = "grpcio-1.66.2-cp310-cp310-win_amd64.whl", hash = "sha256:2018b053aa15782db2541ca01a7edb56a0bf18c77efed975392583725974b249"}, - {file = "grpcio-1.66.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:2335c58560a9e92ac58ff2bc5649952f9b37d0735608242973c7a8b94a6437d8"}, - {file = "grpcio-1.66.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45a3d462826f4868b442a6b8fdbe8b87b45eb4f5b5308168c156b21eca43f61c"}, - {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a9539f01cb04950fd4b5ab458e64a15f84c2acc273670072abe49a3f29bbad54"}, - {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce89f5876662f146d4c1f695dda29d4433a5d01c8681fbd2539afff535da14d4"}, - {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25a14af966438cddf498b2e338f88d1c9706f3493b1d73b93f695c99c5f0e2a"}, - {file = "grpcio-1.66.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6001e575b8bbd89eee11960bb640b6da6ae110cf08113a075f1e2051cc596cae"}, - {file = "grpcio-1.66.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4ea1d062c9230278793820146c95d038dc0f468cbdd172eec3363e42ff1c7d01"}, - {file = "grpcio-1.66.2-cp311-cp311-win32.whl", hash = "sha256:38b68498ff579a3b1ee8f93a05eb48dc2595795f2f62716e797dc24774c1aaa8"}, - {file = "grpcio-1.66.2-cp311-cp311-win_amd64.whl", hash = "sha256:6851de821249340bdb100df5eacfecfc4e6075fa85c6df7ee0eb213170ec8e5d"}, - {file = "grpcio-1.66.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:802d84fd3d50614170649853d121baaaa305de7b65b3e01759247e768d691ddf"}, - {file = "grpcio-1.66.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80fd702ba7e432994df208f27514280b4b5c6843e12a48759c9255679ad38db8"}, - {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:12fda97ffae55e6526825daf25ad0fa37483685952b5d0f910d6405c87e3adb6"}, - {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:950da58d7d80abd0ea68757769c9db0a95b31163e53e5bb60438d263f4bed7b7"}, - {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e636ce23273683b00410f1971d209bf3689238cf5538d960adc3cdfe80dd0dbd"}, - {file = "grpcio-1.66.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a917d26e0fe980b0ac7bfcc1a3c4ad6a9a4612c911d33efb55ed7833c749b0ee"}, - {file = "grpcio-1.66.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49f0ca7ae850f59f828a723a9064cadbed90f1ece179d375966546499b8a2c9c"}, - {file = "grpcio-1.66.2-cp312-cp312-win32.whl", hash = "sha256:31fd163105464797a72d901a06472860845ac157389e10f12631025b3e4d0453"}, - {file = "grpcio-1.66.2-cp312-cp312-win_amd64.whl", hash = "sha256:ff1f7882e56c40b0d33c4922c15dfa30612f05fb785074a012f7cda74d1c3679"}, - {file = "grpcio-1.66.2-cp313-cp313-linux_armv7l.whl", hash = "sha256:3b00efc473b20d8bf83e0e1ae661b98951ca56111feb9b9611df8efc4fe5d55d"}, - {file = "grpcio-1.66.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1caa38fb22a8578ab8393da99d4b8641e3a80abc8fd52646f1ecc92bcb8dee34"}, - {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c408f5ef75cfffa113cacd8b0c0e3611cbfd47701ca3cdc090594109b9fcbaed"}, - {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c806852deaedee9ce8280fe98955c9103f62912a5b2d5ee7e3eaa284a6d8d8e7"}, - {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f145cc21836c332c67baa6fc81099d1d27e266401565bf481948010d6ea32d46"}, - {file = "grpcio-1.66.2-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:73e3b425c1e155730273f73e419de3074aa5c5e936771ee0e4af0814631fb30a"}, - {file = "grpcio-1.66.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9c509a4f78114cbc5f0740eb3d7a74985fd2eff022971bc9bc31f8bc93e66a3b"}, - {file = "grpcio-1.66.2-cp313-cp313-win32.whl", hash = "sha256:20657d6b8cfed7db5e11b62ff7dfe2e12064ea78e93f1434d61888834bc86d75"}, - {file = "grpcio-1.66.2-cp313-cp313-win_amd64.whl", hash = "sha256:fb70487c95786e345af5e854ffec8cb8cc781bcc5df7930c4fbb7feaa72e1cdf"}, - {file = "grpcio-1.66.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:a18e20d8321c6400185b4263e27982488cb5cdd62da69147087a76a24ef4e7e3"}, - {file = "grpcio-1.66.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:02697eb4a5cbe5a9639f57323b4c37bcb3ab2d48cec5da3dc2f13334d72790dd"}, - {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:99a641995a6bc4287a6315989ee591ff58507aa1cbe4c2e70d88411c4dcc0839"}, - {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ed71e81782966ffead60268bbda31ea3f725ebf8aa73634d5dda44f2cf3fb9c"}, - {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbd27c24a4cc5e195a7f56cfd9312e366d5d61b86e36d46bbe538457ea6eb8dd"}, - {file = "grpcio-1.66.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d9a9724a156c8ec6a379869b23ba3323b7ea3600851c91489b871e375f710bc8"}, - {file = "grpcio-1.66.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d8d4732cc5052e92cea2f78b233c2e2a52998ac40cd651f40e398893ad0d06ec"}, - {file = "grpcio-1.66.2-cp38-cp38-win32.whl", hash = "sha256:7b2c86457145ce14c38e5bf6bdc19ef88e66c5fee2c3d83285c5aef026ba93b3"}, - {file = "grpcio-1.66.2-cp38-cp38-win_amd64.whl", hash = "sha256:e88264caad6d8d00e7913996030bac8ad5f26b7411495848cc218bd3a9040b6c"}, - {file = "grpcio-1.66.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:c400ba5675b67025c8a9f48aa846f12a39cf0c44df5cd060e23fda5b30e9359d"}, - {file = "grpcio-1.66.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:66a0cd8ba6512b401d7ed46bb03f4ee455839957f28b8d61e7708056a806ba6a"}, - {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:06de8ec0bd71be123eec15b0e0d457474931c2c407869b6c349bd9bed4adbac3"}, - {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb57870449dfcfac428afbb5a877829fcb0d6db9d9baa1148705739e9083880e"}, - {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b672abf90a964bfde2d0ecbce30f2329a47498ba75ce6f4da35a2f4532b7acbc"}, - {file = "grpcio-1.66.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ad2efdbe90c73b0434cbe64ed372e12414ad03c06262279b104a029d1889d13e"}, - {file = "grpcio-1.66.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c3a99c519f4638e700e9e3f83952e27e2ea10873eecd7935823dab0c1c9250e"}, - {file = "grpcio-1.66.2-cp39-cp39-win32.whl", hash = "sha256:78fa51ebc2d9242c0fc5db0feecc57a9943303b46664ad89921f5079e2e4ada7"}, - {file = "grpcio-1.66.2-cp39-cp39-win_amd64.whl", hash = "sha256:728bdf36a186e7f51da73be7f8d09457a03061be848718d0edf000e709418987"}, - {file = "grpcio-1.66.2.tar.gz", hash = "sha256:563588c587b75c34b928bc428548e5b00ea38c46972181a4d8b75ba7e3f24231"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.66.2)"] - -[[package]] -name = "grpclib" -version = "0.4.7" -description = "Pure-Python gRPC implementation for asyncio" -optional = false -python-versions = ">=3.7" -files = [ - {file = "grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3"}, -] - -[package.dependencies] -h2 = ">=3.1.0,<5" -multidict = "*" - -[package.extras] -protobuf = ["protobuf (>=3.20.0)"] - -[[package]] -name = "h2" -version = "4.1.0" -description = "HTTP/2 State-Machine based protocol implementation" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, - {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, -] - -[package.dependencies] -hpack = ">=4.0,<5" -hyperframe = ">=6.0,<7" - -[[package]] -name = "hpack" -version = "4.0.0" -description = "Pure-Python HPACK header compression" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, - {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, -] - -[[package]] -name = "hyperframe" -version = "6.0.1" -description = "HTTP/2 framing layer for Python" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, - {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, -] - -[[package]] -name = "multidict" -version = "6.1.0" -description = "multidict implementation" -optional = false -python-versions = ">=3.8" -files = [ - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, - {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, - {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, - {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, - {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, - {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, - {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, - {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, - {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, - {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, - {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, - {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, - {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, - {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, - {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "protobuf" -version = "5.28.2" -description = "" -optional = false -python-versions = ">=3.8" -files = [ - {file = "protobuf-5.28.2-cp310-abi3-win32.whl", hash = "sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d"}, - {file = "protobuf-5.28.2-cp310-abi3-win_amd64.whl", hash = "sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132"}, - {file = "protobuf-5.28.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7"}, - {file = "protobuf-5.28.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f"}, - {file = "protobuf-5.28.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f"}, - {file = "protobuf-5.28.2-cp38-cp38-win32.whl", hash = "sha256:87317e9bcda04a32f2ee82089a204d3a2f0d3c8aeed16568c7daf4756e4f1fe0"}, - {file = "protobuf-5.28.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0ea0123dac3399a2eeb1a1443d82b7afc9ff40241433296769f7da42d142ec3"}, - {file = "protobuf-5.28.2-cp39-cp39-win32.whl", hash = "sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36"}, - {file = "protobuf-5.28.2-cp39-cp39-win_amd64.whl", hash = "sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276"}, - {file = "protobuf-5.28.2-py3-none-any.whl", hash = "sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece"}, - {file = "protobuf-5.28.2.tar.gz", hash = "sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0"}, -] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.10" -content-hash = "31da3748cc9a301e6be9cf4f08c1c5fe0d18d5c7dd76c55ec46b28d0073983db" diff --git a/pyold/engine/README b/pyold/engine/README deleted file mode 100644 index 84f0061..0000000 --- a/pyold/engine/README +++ /dev/null @@ -1,15 +0,0 @@ -Euskal Encounter 21 -AI Contest -Motor del juego - -Autor: marcan -Licencia: GPLv2/v3 - -Requisitos: - Python v2.6.x o v2.7.x - # apt-get install python2.7 - PyGame - # apt-get install python-pygame - -Uso: -$ python2.7 engine/game.py maps/ 'comando player0' 'comando player1'... diff --git a/pyold/engine/botplayer.py b/pyold/engine/botplayer.py deleted file mode 100755 index b02e748..0000000 --- a/pyold/engine/botplayer.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/python3 - -import json, subprocess, time, select, sys, os, fcntl -import engine - -class CommError(Exception): - pass - -class BotPlayer(object): - INIT_TIMEOUT = 2.0 - MOVE_TIMEOUT = 0.1 - MOVE_HARDTIMEOUT = 0.5 - def __init__(self, game, playernum, cmdline, debug=False): - self.alive = True - self.p = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) - flag = fcntl.fcntl(self.p.stdout.fileno(), fcntl.F_GETFD) - fcntl.fcntl(self.p.stdout.fileno(), fcntl.F_SETFL, flag | os.O_NONBLOCK) - self.game = game - self.player = game.players[playernum] - self.debug = debug - - def _send(self, data): - line = json.dumps(data).encode("ascii") - assert b"\n" not in line - if self.debug: - print(">>P%d: %r" % (self.player.num, line)) - try: - self.p.stdin.write(line + b"\n") - self.p.stdin.flush() - except: - raise CommError("Error sending data") - - def _recv(self, soft_timeout, hard_timeout): - st = time.time() - et = time.time() + soft_timeout - ht = time.time() + hard_timeout - line = b"" - try: - while not line or line[-1] != 10: - to = max(0, ht - time.time()) - r,w,e = select.select([self.p.stdout],[],[],to) - if self.p.stdout not in r: - raise CommError("Bot %r over hard timeout" % self.player.name) - c = os.read(self.p.stdout.fileno(), 1) - if not c: - raise CommError("Bot closed stdout") - line += c - except Exception as e: - raise CommError("Unknown error: %r" % e) - if time.time() > et: - sys.stderr.write("Bot %r over soft timeout\n" % self.player.name) - try: - if self.debug: - print("< self.MAX_ENERGY: - val = self.MAX_ENERGY - assert val >= 0 - if self[pos]: - self._energymap[y][x] = val - self._energy = _Energy() - - def __getitem__(self, pos): - x, y = pos - if 0 <= x < self.w and 0 <= y < self.h: - return self._island[y][x] - else: - return False - - @property - def energy(self): - return self._energy - - def get_view(self, pos): - px, py = pos - dist = self.HORIZON - view = [] - for y in range(-dist, dist + 1): - row = [] - for x in range(-dist, dist + 1): - if self._horizonmap[y+dist][x+dist]: - row.append(self.energy[px+x, py+y]) - else: - row.append(-1) - view.append(row) - return view - - @property - def map(self): - return self._island - -class Lighthouse(object): - def __init__(self, game, pos): - self.game = game - self.pos = pos - self.owner = None - self.energy = 0 - - def attack(self, player, strength): - if not isinstance(strength, int): - raise MoveError("Strength must be an int") - if strength < 0: - raise MoveError("Strength must be positive") - if strength > player.energy: - strength = player.energy - player.energy -= strength - if self.owner is not None and self.owner != player.num: - d = min(self.energy, strength) - self.decay(d) - strength -= d - if strength: - self.owner = player.num - self.energy += strength - - def decay(self, by): - self.energy -= by - if self.energy <= 0: - self.energy = 0 - self.owner = None - self.game.conns = set(i for i in self.game.conns if self.pos not in i) - self.game.tris = dict(i for i in self.game.tris.items() if self.pos not in i[0]) - -class Player(object): - def __init__(self, game, num, init_pos): - self.num = num - self.game = game - self.pos = init_pos - self.score = 0 - self.energy = 0 - self.keys = set() - self.name = "Player %d" % num - - def move(self, delta): - dx, dy = delta - if dx not in (0, 1, -1) or dy not in (0, 1, -1): - raise MoveError("Delta must be 1 cell away") - new_pos = self.pos[0] + dx, self.pos[1] + dy - if not self.game.island[new_pos]: - raise MoveError("Target pos is not in island") - self.pos = new_pos - -class GameConfig(object): - def __init__(self, mapfile): - with open(mapfile, "r") as fd: - lines = [l.replace("\n", "") for l in fd.readlines()] - self.lighthouses = [] - players = [] - self.island = [] - for y, line in enumerate(lines[::-1]): - row = [] - for x, c in enumerate(line): - if c == "#": - row.append(0) - elif c == "!": - row.append(1) - self.lighthouses.append((x,y)) - elif c == " ": - row.append(1) - else: - row.append(1) - players.append((c, (x,y))) - self.island.append(row) - self.players = [pos for c, pos in sorted(players)] - w = len(self.island[0]) - h = len(self.island) - if not all(len(l) == w for l in self.island): - raise GameError("All map rows must have the same width") - if (not all(not i for i in self.island[0]) or - not all(not i for i in self.island[-1]) or - not all(not (i[0] or i[-1]) for i in self.island)): - raise GameError("Map border must not be part of island") - -class Game(object): - RDIST = 5 - def __init__(self, cfg, numplayers=None): - if numplayers is None: - numplayers = len(cfg.players) - assert numplayers <= len(cfg.players) - self.island = Island(cfg.island) - self.lighthouses = dict((x, Lighthouse(self, x)) for x in cfg.lighthouses) - self.conns = set() - self.tris = dict() - self.players = [Player(self, i, pos) for i, pos in enumerate(cfg.players[:numplayers])] - - def connect(self, player, dest_pos): - if player.pos not in self.lighthouses: - raise MoveError("Player must be located at the origin lighthouse") - if dest_pos not in self.lighthouses: - raise MoveError("Destination must be an existing lighthouse") - orig = self.lighthouses[player.pos] - dest = self.lighthouses[dest_pos] - if orig.owner != player.num or dest.owner != player.num: - raise MoveError("Both lighthouses must be player-owned") - if dest.pos not in player.keys: - raise MoveError("Player does not have the destination key") - if orig is dest: - raise MoveError("Cannot connect lighthouse to itself") - assert orig.energy and dest.energy - pair = frozenset((orig.pos, dest.pos)) - if pair in self.conns: - raise MoveError("Connection already exists") - x0, x1 = sorted((orig.pos[0], dest.pos[0])) - y0, y1 = sorted((orig.pos[1], dest.pos[1])) - for lh in self.lighthouses: - if (x0 <= lh[0] <= x1 and y0 <= lh[1] <= y1 and - lh not in (orig.pos, dest.pos) and - geom.colinear(orig.pos, dest.pos, lh)): - raise MoveError("Connection cannot intersect a lighthouse") - new_tris = set() - for c in self.conns: - if geom.intersect(tuple(c), (orig.pos, dest.pos)): - raise MoveError("Connection cannot intersect another connection") - if orig.pos in c: - third = next(l for l in c if l != orig.pos) - if frozenset((third, dest.pos)) in self.conns: - new_tris.add((orig.pos, dest.pos, third)) - - player.keys.remove(dest.pos) - self.conns.add(pair) - for i in new_tris: - self.tris[i] = [j for j in geom.render(i) if self.island[j]] - - def pre_round(self): - for pos in self.lighthouses: - for y in range(pos[1]-self.RDIST+1, pos[1]+self.RDIST): - for x in range(pos[0]-self.RDIST+1, pos[0]+self.RDIST): - dist = geom.dist(pos, (x,y)) - delta = int(math.floor(self.RDIST - dist)) - if delta > 0: - self.island.energy[x,y] += delta - player_posmap = dict() - for player in self.players: - if player.pos in player_posmap: - player_posmap[player.pos].append(player) - else: - player_posmap[player.pos] = [player] - if player.pos in self.lighthouses: - player.keys.add(player.pos) - for pos, players in player_posmap.items(): - energy = self.island.energy[pos] // len(players) - for player in players: - player.energy += energy - self.island.energy[pos] = 0 - for lh in self.lighthouses.values(): - lh.decay(10) - - def post_round(self): - for lh in self.lighthouses.values(): - if lh.owner is not None: - self.players[lh.owner].score += 2 - for pair in self.conns: - self.players[self.lighthouses[next(iter(pair))].owner].score += 2 - for tri, cells in self.tris.items(): - self.players[self.lighthouses[tri[0]].owner].score += len(cells) diff --git a/pyold/engine/game.py b/pyold/engine/game.py deleted file mode 100755 index 4ba5b13..0000000 --- a/pyold/engine/game.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/python3 - -import sys, time -import engine, botplayer -import view - -cfg_file = sys.argv[1] -bots = sys.argv[2:] -DEBUG = False -CONTINUE_ON_ERROR = False - -config = engine.GameConfig(cfg_file) -game = engine.Game(config, len(bots)) -actors = [botplayer.BotPlayer(game, i, cmdline, debug=DEBUG) for i, cmdline in enumerate(bots)] - -for actor in actors: - actor.initialize() - -view = view.GameView(game) - -round = 0 -while True: - game.pre_round() - view.update() - for actor in actors: - try: - actor.turn() - except botplayer.CommError as e: - if not CONTINUE_ON_ERROR: - raise - else: - print("CommError: " + str(e)) - actor.close() - view.update() - game.post_round() - s = "########### ROUND %d SCORE: " % round - for i in range(len(bots)): - s += "P%d: %d " % (i, game.players[i].score) - print(s) - round += 1 - -view.update() diff --git a/pyold/engine/geom.py b/pyold/engine/geom.py deleted file mode 100755 index 97d4600..0000000 --- a/pyold/engine/geom.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python3 -import math - -def dist(a, b): - x0, y0 = a - x1, y1 = b - return math.sqrt((x0-x1)**2 + (y0-y1)**2) - -def orient2d(a, b, c): - return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]) - -def colinear(a, b, c): - return orient2d(a, b, c) == 0 - -def intersect(j, k): - j1, j2 = j - k1, k2 = k - return ( - orient2d(k1, k2, j1) * orient2d(k1, k2, j2) < 0 and - orient2d(j1, j2, k1) * orient2d(j1, j2, k2) < 0) - -def _bias(p0, p1): - if (p0[1] == p1[1] and p0[0] > p1[0]) or p0[1] > p1[1]: - return 0 - else: - return -1 - -def render(points): - v0, v1, v2 = points - if orient2d(v0, v1, v2) < 0: - v0, v1 = v1, v0 - x0 = min(v0[0], v1[0], v2[0]) - x1 = max(v0[0], v1[0], v2[0]) - y0 = min(v0[1], v1[1], v2[1]) - y1 = max(v0[1], v1[1], v2[1]) - for y in range(y0, y1+1): - for x in range(x0, x1+1): - p = x, y - w0 = orient2d(v1, v2, p) + _bias(v1, v2) - w1 = orient2d(v2, v0, p) + _bias(v2, v0) - w2 = orient2d(v0, v1, p) + _bias(v0, v1) - if w0 >= 0 and w1 >= 0 and w2 >= 0: - yield p - - -def _rendertest(points): - w = 1+max(p[0] for p in points) - h = 1+max(p[1] for p in points) - s = [["."] * w for i in range(h)] - for x,y in render(points): - s[y][x] = "#" - for i,(x,y) in enumerate(points): - s[y][x] = ("ABC","abc")[s[y][x] == "."][i] - for l in s[::-1]: - print("".join(l)) - print() - -if __name__ == "__main__": - assert orient2d((0,0),(0,1),(1,0)) < 0 - assert orient2d((0,1),(1,0),(0,0)) < 0 - assert orient2d((1,0),(0,0),(0,1)) < 0 - assert orient2d((0,1),(0,0),(1,0)) > 0 - assert orient2d((1,0),(0,1),(0,0)) > 0 - assert orient2d((0,0),(1,0),(0,1)) > 0 - assert not intersect(((0,0),(2,2)),((4,1),(1,4))) - assert not intersect(((0,0),(2,2)),((3,1),(1,3))) - assert intersect(((0,0),(2,2)),((2,1),(1,2))) - _rendertest(((0,0),(5,0),(0,5))) - _rendertest(((5,5),(5,0),(0,5))) diff --git a/pyold/engine/view.py b/pyold/engine/view.py deleted file mode 100755 index d1f98b8..0000000 --- a/pyold/engine/view.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/python3 -import pygame, sys, time, math - -CELL = 15 - -PLAYERC = [ - (255, 0, 0), - (0, 0, 255), - (0, 255, 0), - (255, 255, 0), - (0, 255, 255), - (255, 0, 255), - (255, 127, 0), - (255, 127, 127), -] - -class GameView(object): - def __init__(self, game): - self.game = game - pygame.init() - size = width, height = 640, 480 - self.screen = pygame.display.set_mode(size) - self.scale = 1 - self.fw = self.game.island.w * CELL * self.scale - self.fh = self.game.island.h * CELL * self.scale - self.arena = pygame.Surface((self.fw, self.fh), 0, self.screen) - self.nh = self.game.island.h - 1 - - def _afill(self, pos, size, c): - x0, y0 = pos - w, h = size - x0 *= self.scale - y0 *= self.scale - w *= self.scale - h *= self.scale - self.arena.fill(c, (x0, y0, w, h)) - - def _aaline(self, pos1, pos2, c): - x0, y0 = pos1 - x1, y1 = pos2 - x0 *= self.scale - y0 *= self.scale - x1 *= self.scale - y1 *= self.scale - for dx in range(self.scale): - for dy in range(self.scale): - pygame.draw.line(self.arena, c, (x0+dx, y0+dy), (x1+dx, y1+dy)) - - def _diamond(self, pos, size, c, width=0): - cx, cy = pos - cx *= self.scale - cy *= self.scale - size *= self.scale - points = [ - (cx - size, cy), - (cx, cy - size), - (cx + size, cy), - (cx, cy + size), - ] - pygame.draw.polygon(self.arena, c, points, width) - - def cmul(self, col, mul): - r, g, b = col - return int(r * mul), int(g * mul), int(b * mul) - - def calpha(self, col1, col2, a): - r1, g1, b1 = col1 - r2, g2, b2 = col2 - return (int(r2 * a + r1 * (1-a)), - int(g2 * a + g1 * (1-a)), - int(b2 * a + b1 * (1-a))) - - def draw_cell(self, pos): - cx, cy = pos - py = (self.nh - cy) * CELL - px = cx * CELL - c = int(self.game.island.energy[cx, cy] / 100.0 * 25) - bg = tuple(map(int,(25+c*0.8, 25+c*0.8, 25+c))) - - for vertices, fill in self.game.tris.items(): - if (cx, cy) in fill: - owner = self.game.lighthouses[vertices[0]].owner - bg = self.calpha(bg, PLAYERC[owner], 0.15) - - self._afill((px, py), (CELL, CELL), bg) - self._afill((px + CELL//2, py + CELL//2), (1,1), (255,255,255)) - - cplayers = [i for i in self.game.players if i.pos == (cx, cy)] - if cplayers: - nx = int(math.ceil(math.sqrt(len(cplayers)))) - wx = (CELL - 4) / nx - ny = int(math.ceil(len(cplayers)/nx)) - wy = (CELL - 4) / ny - for i, player in enumerate(cplayers): - iy = i // nx - ix = i % nx - color = self.cmul(PLAYERC[player.num], 0.5) - sx, sy = int(px + 2 + ix * wx), int(py + 2 + iy * wy) - ex, ey = int(px + 2 + (ix + 1) * wx), int(py + 2 + (iy + 1) * wy) - self._afill((sx, sy), (ex - sx, ey - sy), color) - - if (cx, cy) in self.game.lighthouses: - lh = self.game.lighthouses[cx, cy] - color = (192, 192, 192) - if lh.owner is not None: - color = PLAYERC[lh.owner] - self._diamond((px + CELL//2, py + CELL//2), 4, color, 0) - - def update(self): - self.arena.fill((0, 0, 0)) - for cy in range(self.game.island.h): - for cx in range(self.game.island.w): - if self.game.island[cx, cy]: - self.draw_cell((cx, cy)) - for (x0, y0), (x1, y1) in self.game.conns: - owner = self.game.lighthouses[x0, y0].owner - color = PLAYERC[owner] - y0, y1 = self.nh - y0, self.nh - y1 - self._aaline((x0 * CELL + CELL//2, y0 * CELL + CELL//2), - (x1 * CELL + CELL//2, y1 * CELL + CELL//2), color) - self.screen.blit(self.arena, (0,0)) - pygame.display.flip() diff --git a/pyold/examples/RandBot/README b/pyold/examples/RandBot/README deleted file mode 100644 index 59cf0c2..0000000 --- a/pyold/examples/RandBot/README +++ /dev/null @@ -1,13 +0,0 @@ -Nombre: RandBot v0.1 -Autor: marcan -Puesto: Control de Servidores -Licencia: Dominio Público - -Robot de ejemplo que juega de forma aleatoria - -Requisitos: - Python v2.6.x o v2.7.x - # apt-get install python2.7 - -Uso: -$ python2.7 RandBot/randbot.py diff --git a/pyold/examples/RandBot/interface.py b/pyold/examples/RandBot/interface.py deleted file mode 100755 index 10fc9e7..0000000 --- a/pyold/examples/RandBot/interface.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -import sys, json - -# ============================================================================== -# ROBOT -# Los robots definidos deben heredar de esta clase. -# ============================================================================== - -class Bot(object): - """Bot base. Este bot no hace nada (pasa todos los turnos).""" - NAME = "NullBot" - - # ========================================================================== - # Comportamiento del bot - # Métodos a implementar / sobreescribir (opcionalmente) - # ========================================================================== - - def __init__(self, init_state): - """Inicializar el bot: llamado al comienzo del juego.""" - self.player_num = init_state["player_num"] - self.player_count = init_state["player_count"] - self.init_pos = init_state["position"] - self.map = init_state["map"] - self.lighthouses = map(tuple, init_state["lighthouses"]) - - def play(self, state): - """Jugar: llamado cada turno. - Debe devolver una acción (jugada). - - state: estado actual del juego. - """ - return self.nop() - - def success(self): - """Éxito: llamado cuando la jugada previa es válida.""" - pass - - def error(self, message, last_move): - """Error: llamado cuando la jugada previa no es válida.""" - self.log("Recibido error: %s", message) - self.log("Jugada previa: %r", last_move) - - # ========================================================================== - # Utilidades - # No es necesario sobreescribir estos métodos. - # ========================================================================== - - def log(self, message, *args): - """Mostrar mensaje de registro por stderr""" - print >>sys.stderr, "[%s] %s" % (self.NAME, (message % args)) - - # ========================================================================== - # Jugadas posibles - # No es necesario sobreescribir estos métodos. - # ========================================================================== - - def nop(self): - """Pasar el turno""" - return { - "command": "pass", - } - - def move(self, x, y): - """Mover a una casilla adyacente - - x: delta x (0, -1, 1) - y: delta y (0, -1, 1) - """ - return { - "command": "move", - "x": x, - "y": y - } - - def attack(self, energy): - """Atacar a un faro - - energy: energía (entero positivo) - """ - return { - "command": "attack", - "energy": energy - } - - def connect(self, destination): - """Conectar a un faro remoto - - destination: tupla o lista (x,y): coordenadas del faro remoto - """ - return { - "command": "connect", - "destination": destination - } - -# ============================================================================== -# Interfaz -# ============================================================================== - -class Interface(object): - def __init__(self, bot_class): - self.bot_class = bot_class - self.bot = None - - def _recv(self): - line = sys.stdin.readline() - if not line: - sys.exit(0) - return json.loads(line) - - def _send(self, msg): - sys.stdout.write(json.dumps(msg) + "\n") - sys.stdout.flush() - - def run(self): - init = self._recv() - self.bot = self.bot_class(init) - self._send({"name": self.bot.NAME}) - while True: - state = self._recv() - move = self.bot.play(state) - self._send(move) - status = self._recv() - if status["success"]: - self.bot.success() - else: - self.bot.error(status["message"], move) - -if __name__ == "__main__": - iface = Interface(Bot) - iface.run() diff --git a/pyold/examples/RandBot/randbot.py b/pyold/examples/RandBot/randbot.py deleted file mode 100755 index d5529fd..0000000 --- a/pyold/examples/RandBot/randbot.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -import random, sys -import interface - -class RandBot(interface.Bot): - """Bot que juega aleatoriamente.""" - NAME = "RandBot" - - def play(self, state): - """Jugar: llamado cada turno. - Debe devolver una acción (jugada).""" - cx, cy = state["position"] - lighthouses = dict((tuple(lh["position"]), lh) - for lh in state["lighthouses"]) - - # Si estamos en un faro... - if (cx, cy) in self.lighthouses: - # Probabilidad 60%: conectar con faro remoto válido - if lighthouses[(cx, cy)]["owner"] == self.player_num: - if random.randrange(100) < 60: - possible_connections = [] - for dest in self.lighthouses: - # No conectar con sigo mismo - # No conectar si no tenemos la clave - # No conectar si ya existe la conexión - # No conectar si no controlamos el destino - # Nota: no comprobamos si la conexión se cruza. - if (dest != (cx, cy) and - lighthouses[dest]["have_key"] and - [cx, cy] not in lighthouses[dest]["connections"] and - lighthouses[dest]["owner"] == self.player_num): - possible_connections.append(dest) - - if possible_connections: - return self.connect(random.choice(possible_connections)) - - # Probabilidad 60%: recargar el faro - if random.randrange(100) < 60: - energy = random.randrange(state["energy"] + 1) - return self.attack(energy) - - # Mover aleatoriamente - moves = ((-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)) - # Determinar movimientos válidos - moves = [(x,y) for x,y in moves if self.map[cy+y][cx+x]] - move = random.choice(moves) - return self.move(*move) - -if __name__ == "__main__": - iface = interface.Interface(RandBot) - iface.run() diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 3ba4ea7..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,17 +0,0 @@ -[tool.poetry] -name = "lighthouses-aicontest" -version = "0.1.0" -description = "" -authors = ["Javier Gil "] -readme = "README.md" - -[tool.poetry.dependencies] -python = "^3.10" -grpclib = "^0.4.7" -protobuf = "^5.28.2" -grpcio = "^1.66.2" - - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index f532a96..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -grpcio -protobuf diff --git a/start-game.sh b/start-game.sh index fb6771e..fa019ad 100755 --- a/start-game.sh +++ b/start-game.sh @@ -22,7 +22,7 @@ MAPS_DIR="${REPO_DIR}/maps" LOG_DIR="${REPO_DIR}/log" OUTPUT_DIR="${REPO_DIR}/output" GAME_TIMESTAMP="$(date +%Y%m%d-%H%M%S)" -DOCKERFILE_GAME="${REPO_DIR}/docker/Dockerfile.game" +DOCKERFILE_GAME="${REPO_DIR}/Dockerfile" DOCKER_COMPOSE_FILE="game-${GAME_TIMESTAMP}.yaml" LOG_FILE="${LOG_DIR}/game-${GAME_TIMESTAMP}.log" COMMAND_UP="docker compose -f ${DOCKER_COMPOSE_FILE} up --timestamps --abort-on-container-exit"