├── starter-bot-python ├── client │ ├── __init__.py │ ├── message │ │ ├── __init__.py │ │ ├── base.py │ │ ├── extra_types.py │ │ ├── messages.py │ │ └── factory.py │ ├── bot_match_runner.py │ ├── bot_base.py │ ├── socket_session.py │ └── client.py ├── README.md ├── main.py └── bot.py ├── .gitignore ├── server ├── hypernull.properties ├── src │ └── main │ │ └── java │ │ └── ru │ │ └── croccode │ │ └── hypernull │ │ ├── map │ │ ├── MapRegistry.java │ │ ├── RandomMapRegistry.java │ │ ├── MapWriter.java │ │ ├── MapReader.java │ │ ├── MapStore.java │ │ ├── MapBuilder.java │ │ └── RandomMap.java │ │ ├── server │ │ ├── MatchId.java │ │ ├── ThreadPools.java │ │ ├── MatchRequest.java │ │ ├── Server.java │ │ ├── MatchFileLogger.java │ │ ├── AsciiMatchPrinter.java │ │ ├── MatchRunner.java │ │ └── HyperNull.java │ │ └── match │ │ ├── MatchListener.java │ │ ├── Bot.java │ │ ├── MapIndex.java │ │ └── MatchConfig.java └── pom.xml ├── starter-bot ├── bot.properties ├── src │ └── main │ │ └── java │ │ └── ru │ │ └── croccode │ │ └── hypernull │ │ └── bot │ │ ├── Bot.java │ │ ├── DeathmatchLoop.java │ │ ├── BotConfig.java │ │ ├── BotMatchRunner.java │ │ └── StarterBot.java └── pom.xml ├── common ├── src │ └── main │ │ └── java │ │ └── ru │ │ └── croccode │ │ └── hypernull │ │ ├── message │ │ ├── MatchOver.java │ │ ├── Message.java │ │ ├── Move.java │ │ ├── Hello.java │ │ ├── Register.java │ │ ├── Update.java │ │ └── MatchStarted.java │ │ ├── domain │ │ ├── MatchMode.java │ │ └── MatchMap.java │ │ ├── geometry │ │ ├── Offset.java │ │ ├── Size.java │ │ └── Point.java │ │ ├── util │ │ ├── Strings.java │ │ ├── Silent.java │ │ └── Check.java │ │ └── io │ │ ├── SocketSession.java │ │ └── Session.java └── pom.xml ├── map-generator ├── src │ └── main │ │ └── java │ │ └── MapGenerator │ │ ├── Strategy.java │ │ ├── MapGenerator.java │ │ ├── Parameter.java │ │ ├── Main.java │ │ ├── MapGeneratorImpl.java │ │ ├── Saver.java │ │ ├── Map.java │ │ └── CellAutomataStrategy.java └── pom.xml ├── maze-generator ├── src │ └── main │ │ └── java │ │ ├── main.java │ │ ├── Point.java │ │ ├── InitSettings.java │ │ └── WriteMap.java └── pom.xml ├── player ├── src │ └── main │ │ ├── resources │ │ └── style.css │ │ └── java │ │ └── ru │ │ └── croccode │ │ └── hypernull │ │ └── player │ │ ├── model │ │ ├── PlaybackModel.java │ │ ├── MatchModel.java │ │ └── MatchLog.java │ │ ├── Styles.java │ │ ├── controller │ │ └── PlaybackController.java │ │ ├── view │ │ ├── InfoView.java │ │ ├── PlaybackView.java │ │ └── MatchView.java │ │ ├── MatchLogParser.java │ │ └── App.java └── pom.xml ├── pom.xml ├── TASKS.md └── maps ├── random_3_2.map ├── random_2_2.map ├── random_4_2.map ├── random_2_1.map ├── map_2_4.map ├── map_2_1.map ├── map_2_3.map ├── random_6_2.map ├── random_3_1.map ├── random_4_1.map └── map_2_2.map /starter-bot-python/client/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /starter-bot-python/client/message/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | target/ 3 | out/ 4 | .idea/ 5 | *.iml 6 | matchlogs/ 7 | -------------------------------------------------------------------------------- /server/hypernull.properties: -------------------------------------------------------------------------------- 1 | server.port=2021 2 | match.log.stdout=false 3 | match.log.folder=./matchlogs/ 4 | maps.folder=./maps/ 5 | -------------------------------------------------------------------------------- /starter-bot/bot.properties: -------------------------------------------------------------------------------- 1 | server.host=localhost 2 | server.port=2021 3 | bot.name=starter-bot 4 | bot.secret= 5 | mode=FRIENDLY 6 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/message/MatchOver.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.message; 2 | 3 | public class MatchOver extends Message { 4 | } 5 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/domain/MatchMode.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.domain; 2 | 3 | public enum MatchMode { 4 | FRIENDLY, 5 | DEATHMATCH; 6 | } 7 | -------------------------------------------------------------------------------- /map-generator/src/main/java/MapGenerator/Strategy.java: -------------------------------------------------------------------------------- 1 | package MapGenerator; 2 | 3 | public interface Strategy { 4 | public Map generateMap(int height, int width); 5 | } 6 | -------------------------------------------------------------------------------- /maze-generator/src/main/java/main.java: -------------------------------------------------------------------------------- 1 | public class main { 2 | public static void main(String[] args) { 3 | System.out.println("Карта успешно создана"); 4 | CreateMap map=new CreateMap(180,240); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/map/MapRegistry.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.map; 2 | 3 | import ru.croccode.hypernull.domain.MatchMap; 4 | 5 | public interface MapRegistry { 6 | 7 | MatchMap randomMap(int numBots); 8 | } 9 | -------------------------------------------------------------------------------- /map-generator/src/main/java/MapGenerator/MapGenerator.java: -------------------------------------------------------------------------------- 1 | package MapGenerator; 2 | 3 | public interface MapGenerator { 4 | 5 | public MapGenerator setStrategy(Strategy strategy); 6 | public Map generateMap(int height, int width); 7 | } 8 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/message/Message.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.message; 2 | 3 | import java.util.List; 4 | 5 | public abstract class Message { 6 | 7 | @Override 8 | public String toString() { 9 | List lines = Messages.format(this); 10 | return String.join("\n", lines); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/map/RandomMapRegistry.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.map; 2 | 3 | import ru.croccode.hypernull.domain.MatchMap; 4 | 5 | public class RandomMapRegistry implements MapRegistry { 6 | 7 | @Override 8 | public MatchMap randomMap(int numBots) { 9 | return new RandomMap(numBots); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /starter-bot-python/README.md: -------------------------------------------------------------------------------- 1 | ## 🐍 Python Starter bot 2 | 3 | ### Как использовать 4 | 5 | [`main.py`](main.py) - настройки подключения к серверу 6 | 7 | [`bot.py`](bot.py) - код бота и его параметры 8 | 9 | Требования: **Python 3.10+** 10 | 11 | --- 12 | _[Артем **nGragas** Корников](https://t.me/Rush_iam) для [Croc Bot Battle](https://brainz.croc.ru/hello-work)_ -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/message/Move.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.message; 2 | 3 | import ru.croccode.hypernull.geometry.Offset; 4 | 5 | public class Move extends Message { 6 | 7 | private Offset offset; 8 | 9 | public Offset getOffset() { 10 | return offset; 11 | } 12 | 13 | public void setOffset(Offset offset) { 14 | this.offset = offset; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /starter-bot-python/main.py: -------------------------------------------------------------------------------- 1 | from bot import Bot, bot_name, bot_secret, mode 2 | from client.bot_match_runner import BotMatchRunner 3 | from client.client import HypernullClient 4 | 5 | server_host = 'localhost' 6 | server_port = 2021 7 | 8 | if __name__ == '__main__': 9 | BotMatchRunner( 10 | bot=Bot(bot_name, bot_secret, mode), 11 | client=HypernullClient(server_host, server_port), 12 | ).run() 13 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/message/Hello.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.message; 2 | 3 | public class Hello extends Message { 4 | 5 | private Integer protocolVersion = Messages.PROTOCOL_VERSION; 6 | 7 | public Integer getProtocolVersion() { 8 | return protocolVersion; 9 | } 10 | 11 | public void setProtocolVersion(Integer protocolVersion) { 12 | this.protocolVersion = protocolVersion; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /maze-generator/src/main/java/Point.java: -------------------------------------------------------------------------------- 1 | import java.util.Set; 2 | 3 | public class Point { 4 | private int x; 5 | private int y; 6 | 7 | public Point(int x, int y) { 8 | this.x = x; 9 | this.y = y; 10 | } 11 | 12 | public int getX() { 13 | return x; 14 | } 15 | 16 | public int getY() { 17 | return y; 18 | } 19 | 20 | public String toString() { 21 | return x + " " + y; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /map-generator/src/main/java/MapGenerator/Parameter.java: -------------------------------------------------------------------------------- 1 | package MapGenerator; 2 | 3 | public class Parameter { 4 | final static Character FLOOR_SYMBOL = '.'; 5 | final static Boolean FLOOR = true; 6 | final static Character WALL_SYMBOL = '■'; 7 | final static Boolean WALL = false; 8 | final static int INITIAL_PERCENTAGE = 35; 9 | final static int AUTOMATA_DEPTH = 1; 10 | final static int LAYER_DEPTH = 7; 11 | final static int SPAWNPOINTS = 4; 12 | } 13 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ru.croccode.hypernull 8 | hypernull 9 | 1.0-SNAPSHOT 10 | 11 | common 12 | 1.0-SNAPSHOT 13 | 14 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/geometry/Offset.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.geometry; 2 | 3 | public class Offset { 4 | 5 | private final int dx; 6 | private final int dy; 7 | 8 | public Offset(int dx, int dy) { 9 | this.dx = dx; 10 | this.dy = dy; 11 | } 12 | 13 | public int dx() { 14 | return dx; 15 | } 16 | 17 | public int dy() { 18 | return dy; 19 | } 20 | 21 | public int length2() { 22 | return dx * dx + dy * dy; 23 | } 24 | 25 | public double length() { 26 | return Math.sqrt(length2()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/geometry/Size.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.geometry; 2 | 3 | import ru.croccode.hypernull.util.Check; 4 | 5 | public class Size { 6 | 7 | private final int width; 8 | private final int height; 9 | 10 | public Size(int width, int height) { 11 | Check.condition(width >= 0); 12 | Check.condition(height >= 0); 13 | 14 | this.width = width; 15 | this.height = height; 16 | } 17 | 18 | public int width() { 19 | return width; 20 | } 21 | 22 | public int height() { 23 | return height; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /map-generator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ru.croccode.hypernull 8 | hypernull 9 | 1.0-SNAPSHOT 10 | 11 | map-generator 12 | 1.0-SNAPSHOT 13 | 14 | -------------------------------------------------------------------------------- /maze-generator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | hypernull 7 | ru.croccode.hypernull 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | create-map 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /starter-bot-python/client/message/base.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, asdict 2 | 3 | 4 | @dataclass 5 | class MessageBase: 6 | @classmethod 7 | def type(cls) -> str: 8 | return cls.__name__.lower() 9 | 10 | def dump(self) -> str: 11 | command = self.type() 12 | params = '\n'.join( 13 | f'{k} {" ".join(map(str, v.values())) if isinstance(v, dict) else v}' 14 | for k, v in asdict(self).items() if v not in [None, ''] 15 | ) 16 | return f'{command}\n' \ 17 | f'{params}\n' \ 18 | f'end\n' 19 | -------------------------------------------------------------------------------- /starter-bot-python/client/message/extra_types.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from enum import Enum 3 | 4 | 5 | class Mode(str, Enum): 6 | FRIENDLY = 'FRIENDLY' 7 | DEATHMATCH = 'DEATHMATCH' 8 | 9 | 10 | @dataclass 11 | class XY: 12 | x: int 13 | y: int 14 | 15 | def __post_init__(self): 16 | self.x = int(self.x) 17 | self.y = int(self.y) 18 | 19 | 20 | @dataclass 21 | class BotInfo(XY): 22 | coins: int 23 | id: int 24 | 25 | def __post_init__(self): 26 | super().__post_init__() 27 | self.coins = int(self.coins) 28 | self.id = int(self.id) 29 | -------------------------------------------------------------------------------- /starter-bot/src/main/java/ru/croccode/hypernull/bot/Bot.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.bot; 2 | 3 | import ru.croccode.hypernull.message.Hello; 4 | import ru.croccode.hypernull.message.MatchOver; 5 | import ru.croccode.hypernull.message.MatchStarted; 6 | import ru.croccode.hypernull.message.Move; 7 | import ru.croccode.hypernull.message.Register; 8 | import ru.croccode.hypernull.message.Update; 9 | 10 | public interface Bot { 11 | 12 | Register onHello(Hello hello); 13 | 14 | void onMatchStarted(MatchStarted matchStarted); 15 | 16 | Move onUpdate(Update update); 17 | 18 | void onMatchOver(MatchOver matchOver); 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/util/Strings.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.util; 2 | 3 | public final class Strings { 4 | 5 | private static final String EMPTY_STRING = ""; 6 | 7 | private Strings() { 8 | } 9 | 10 | public static String empty() { 11 | return EMPTY_STRING; 12 | } 13 | 14 | public static boolean isNullOrEmpty(String str) { 15 | return str == null || str.isEmpty(); 16 | } 17 | 18 | public static String nullToEmpty(String str) { 19 | return str == null ? EMPTY_STRING : str; 20 | } 21 | 22 | public static String emptyToNull(String str) { 23 | return str != null && str.isEmpty() ? null : str; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /starter-bot-python/client/bot_match_runner.py: -------------------------------------------------------------------------------- 1 | from .bot_base import BotImpl 2 | from .client import HypernullClient 3 | from .message import messages 4 | 5 | 6 | class BotMatchRunner: 7 | def __init__(self, bot: BotImpl, client: HypernullClient): 8 | self.bot = bot 9 | self.client = client 10 | 11 | def run(self) -> None: 12 | self.client.register(self.bot) 13 | 14 | match_info: messages.MatchStarted = self.client.get() 15 | self.bot.on_match_start(match_info) 16 | 17 | while update := self.client.get_update(): 18 | dx, dy = self.bot.on_update(update) 19 | self.client.move(dx, dy) 20 | 21 | self.bot.on_match_over() 22 | -------------------------------------------------------------------------------- /player/src/main/resources/style.css: -------------------------------------------------------------------------------- 1 | #playback-slider .thumb{ 2 | -fx-pref-height: 16; 3 | -fx-pref-width: 10; 4 | -fx-padding: 0; 5 | -fx-background-image: none; 6 | -fx-background-color: rgb(200, 200, 200); 7 | -fx-background-radius: 0 0 0 0; 8 | } 9 | 10 | #playback-slider .track { 11 | -fx-pref-height: 8; 12 | -fx-background-color: rgb(99, 99, 99); 13 | -fx-background-radius: 0 0 0 0; 14 | } 15 | 16 | .check-box .box { 17 | -fx-background-color: rgb(99, 99, 99); 18 | -fx-border-color: none; 19 | -fx-border-radius: 0; 20 | } 21 | 22 | .check-box:selected .box { 23 | -fx-background-color: rgb(99, 99, 99); 24 | } 25 | 26 | .check-box:selected .mark { 27 | -fx-background-color: rgb(200, 200, 200); 28 | } 29 | -------------------------------------------------------------------------------- /map-generator/src/main/java/MapGenerator/Main.java: -------------------------------------------------------------------------------- 1 | package MapGenerator; 2 | 3 | import java.io.IOException; 4 | import java.util.Scanner; 5 | 6 | public class Main { 7 | 8 | public static void main(String[] args) throws IOException { 9 | while(true) { 10 | 11 | MapGenerator mapgen = new MapGeneratorImpl(); 12 | mapgen.setStrategy(new CellAutomataStrategy()); 13 | 14 | Scanner scan = new Scanner(System.in); 15 | 16 | int height = scan.nextInt(), width = scan.nextInt(); 17 | Map map = mapgen.generateMap(height, width); 18 | map.print(); 19 | 20 | if (scan.nextInt() == 1) 21 | Saver.save(map); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/server/MatchId.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.server; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | public final class MatchId { 6 | 7 | private final static AtomicInteger COUNTER = new AtomicInteger(0); 8 | 9 | private MatchId() { 10 | } 11 | 12 | public static String nextId() { 13 | // format: current time (epoch millis) 14 | // + 2 chars of sync match counter reminder 15 | // separated by underscore 16 | int n = 'z' - 'a' + 1; 17 | int mod = n * n; 18 | int k = COUNTER.getAndIncrement() % mod; 19 | if (k < 0) 20 | k += mod; 21 | long now = System.currentTimeMillis(); 22 | return now 23 | + "_" 24 | + (char)('a' + (k / n)) 25 | + (char)('a' + (k % n)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/match/MatchListener.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.match; 2 | 3 | import java.util.Map; 4 | 5 | import ru.croccode.hypernull.domain.MatchMap; 6 | import ru.croccode.hypernull.geometry.Point; 7 | 8 | public interface MatchListener { 9 | 10 | void matchStarted(String id, MatchMap map, MatchConfig config, Map botNames); 11 | 12 | void matchRound(int round); 13 | 14 | void coinSpawned(Point position); 15 | 16 | void coinCollected(Point position, K botKey); 17 | 18 | void botSpawned(K botKey, Point position); 19 | 20 | void botMoved(K botKey, Point position); 21 | 22 | void attack(K attackingKey, K defendingKey); 23 | 24 | void botCoinsChanged(K botKey, int numCoins); 25 | 26 | void matchOver(K botKey); 27 | } 28 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/message/Register.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.message; 2 | 3 | import ru.croccode.hypernull.domain.MatchMode; 4 | 5 | public class Register extends Message { 6 | 7 | private String botName; 8 | 9 | private String botSecret; 10 | 11 | private MatchMode mode; 12 | 13 | public String getBotName() { 14 | return botName; 15 | } 16 | 17 | public void setBotName(String botName) { 18 | this.botName = botName; 19 | } 20 | 21 | public String getBotSecret() { 22 | return botSecret; 23 | } 24 | 25 | public void setBotSecret(String botSecret) { 26 | this.botSecret = botSecret; 27 | } 28 | 29 | public MatchMode getMode() { 30 | return mode; 31 | } 32 | 33 | public void setMode(MatchMode mode) { 34 | this.mode = mode; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /starter-bot-python/client/bot_base.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | from typing import TypeVar 3 | 4 | from client.message.extra_types import Mode 5 | from client.message.messages import MatchStarted, Update 6 | 7 | 8 | BotImpl = TypeVar('BotImpl', bound='BotBase') 9 | 10 | 11 | @dataclass 12 | class BotBase: 13 | name: str 14 | secret: str = None 15 | mode: Mode = Mode.FRIENDLY 16 | match_info: MatchStarted = field(init=False) 17 | id: int = field(init=False) 18 | 19 | def on_match_start(self, match_info: MatchStarted) -> None: 20 | raise NotImplementedError 21 | 22 | def on_update(self, update: Update) -> tuple[int, int]: 23 | raise NotImplementedError 24 | 25 | def on_match_over(self) -> None: 26 | raise NotImplementedError 27 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/server/ThreadPools.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.server; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | public final class ThreadPools { 7 | 8 | private static final int MAX_PARALLEL_MATCHES = 20; 9 | 10 | private static final ExecutorService DEFAULT_POOL = Executors.newCachedThreadPool(); 11 | 12 | private static final ExecutorService MATCH_POOL = Executors.newFixedThreadPool(MAX_PARALLEL_MATCHES); 13 | 14 | private ThreadPools() { 15 | } 16 | 17 | public static ExecutorService defaultPool() { 18 | return DEFAULT_POOL; 19 | } 20 | 21 | public static ExecutorService matchPool() { 22 | return MATCH_POOL; 23 | } 24 | 25 | public static void shutdownAll() { 26 | MATCH_POOL.shutdownNow(); 27 | DEFAULT_POOL.shutdownNow(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/server/MatchRequest.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.server; 2 | 3 | import ru.croccode.hypernull.domain.MatchMode; 4 | import ru.croccode.hypernull.io.SocketSession; 5 | import ru.croccode.hypernull.util.Check; 6 | 7 | public class MatchRequest { 8 | 9 | private final String botName; 10 | 11 | private final SocketSession session; 12 | 13 | private final MatchMode mode; 14 | 15 | public MatchRequest(String botName, SocketSession session, MatchMode mode) { 16 | Check.notEmpty(botName); 17 | Check.notNull(session); 18 | Check.notNull(mode); 19 | 20 | this.botName = botName; 21 | this.session = session; 22 | this.mode = mode; 23 | } 24 | 25 | public String getBotName() { 26 | return botName; 27 | } 28 | 29 | public SocketSession getSession() { 30 | return session; 31 | } 32 | 33 | public MatchMode getMode() { 34 | return mode; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/util/Silent.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.util; 2 | 3 | import java.util.function.Supplier; 4 | 5 | public final class Silent { 6 | 7 | private Silent() { 8 | } 9 | 10 | public static Runnable runnableOf(Block block) { 11 | return () -> { 12 | try { 13 | block.invoke(); 14 | } catch (Exception e) { 15 | e.printStackTrace(); 16 | } 17 | }; 18 | } 19 | 20 | public static Supplier supplierOf(BlockWithResult block) { 21 | return () -> { 22 | try { 23 | return block.invoke(); 24 | } catch (Exception e) { 25 | e.printStackTrace(); 26 | } 27 | return null; 28 | }; 29 | } 30 | 31 | @FunctionalInterface 32 | public interface Block { 33 | 34 | void invoke() throws Exception; 35 | } 36 | 37 | @FunctionalInterface 38 | public interface BlockWithResult { 39 | 40 | T invoke() throws Exception; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /player/src/main/java/ru/croccode/hypernull/player/model/PlaybackModel.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.player.model; 2 | 3 | import javafx.beans.property.SimpleBooleanProperty; 4 | import javafx.beans.property.SimpleIntegerProperty; 5 | 6 | public class PlaybackModel { 7 | 8 | private final SimpleIntegerProperty round = new SimpleIntegerProperty(0); 9 | 10 | private final SimpleIntegerProperty numRounds = new SimpleIntegerProperty(0); 11 | 12 | private final SimpleBooleanProperty paused = new SimpleBooleanProperty(true); 13 | 14 | private final SimpleBooleanProperty fog = new SimpleBooleanProperty(false); 15 | 16 | public SimpleIntegerProperty getRound() { 17 | return round; 18 | } 19 | 20 | public SimpleIntegerProperty getNumRounds() { 21 | return numRounds; 22 | } 23 | 24 | public SimpleBooleanProperty getPaused() { 25 | return paused; 26 | } 27 | 28 | public SimpleBooleanProperty getFog() { 29 | return fog; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/domain/MatchMap.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.domain; 2 | 3 | import java.util.List; 4 | 5 | import ru.croccode.hypernull.geometry.Point; 6 | import ru.croccode.hypernull.geometry.Size; 7 | 8 | public interface MatchMap { 9 | 10 | int DEFAULT_VIEW_RADIUS = 6; 11 | int DEFAULT_MINING_RADIUS = 2; 12 | int DEFAULT_ATTACK_RADIUS = 4; 13 | 14 | Size getSize(); 15 | 16 | default int getWidth() { 17 | return getSize().width(); 18 | } 19 | 20 | default int getHeight() { 21 | return getSize().height(); 22 | } 23 | 24 | boolean isBlocked(Point point); 25 | 26 | default boolean isBlocked(int x, int y) { 27 | return isBlocked(new Point(x, y)); 28 | } 29 | 30 | default int getViewRadius() { 31 | return DEFAULT_VIEW_RADIUS; 32 | } 33 | 34 | default int getMiningRadius() { 35 | return DEFAULT_MINING_RADIUS; 36 | } 37 | 38 | default int getAttackRadius() { 39 | return DEFAULT_ATTACK_RADIUS; 40 | } 41 | 42 | List getSpawnPositions(); 43 | } 44 | -------------------------------------------------------------------------------- /map-generator/src/main/java/MapGenerator/MapGeneratorImpl.java: -------------------------------------------------------------------------------- 1 | package MapGenerator; 2 | 3 | public class MapGeneratorImpl implements MapGenerator { 4 | private Strategy strategy; 5 | 6 | @Override 7 | public MapGenerator setStrategy(Strategy strategy) { 8 | this.strategy = strategy; 9 | return this; 10 | } 11 | @Override 12 | public Map generateMap(int height, int width) { 13 | Map map = strategy.generateMap(height, width); 14 | setSpawnpoints(map, Parameter.SPAWNPOINTS); 15 | return map; 16 | } 17 | 18 | public void setSpawnpoints(Map map, int num) { 19 | int y, x; 20 | for(int point=0; point < num/2; point++) { 21 | do { 22 | y = (int) (Math.random() * (map.getHeight()-2)+2); 23 | x = (int) (Math.random() * (map.getWidth()-2)+2); 24 | } while(!map.get(y,x)); 25 | 26 | map.setSpawnpoint(y,x); 27 | map.setSpawnpoint(map.getHeight()-1-y,map.getWidth()-1-x); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /starter-bot/src/main/java/ru/croccode/hypernull/bot/DeathmatchLoop.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.bot; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.Socket; 6 | import java.nio.file.Paths; 7 | 8 | import ru.croccode.hypernull.io.SocketSession; 9 | 10 | public class DeathmatchLoop { 11 | 12 | public static void main(String[] args) throws IOException { 13 | String configPath = args.length > 0 14 | ? args[0] 15 | : "bot.properties"; 16 | BotConfig botConfig = BotConfig.load(Paths.get(configPath)); 17 | 18 | while (true) { 19 | try { 20 | Socket socket = new Socket(); 21 | socket.setTcpNoDelay(true); 22 | socket.setSoTimeout(300_000); 23 | socket.connect(new InetSocketAddress( 24 | botConfig.getServerHost(), 25 | botConfig.getServerPort())); 26 | 27 | SocketSession session = new SocketSession(socket); 28 | StarterBot bot = new StarterBot(botConfig.getBotName(), botConfig.getMode()); 29 | new BotMatchRunner(bot, session).run(); 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /starter-bot-python/client/message/messages.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | 3 | from . import extra_types 4 | from .base import MessageBase 5 | 6 | 7 | @dataclass 8 | class Hello(MessageBase): 9 | protocol_version: int 10 | 11 | 12 | @dataclass 13 | class Register(MessageBase): 14 | bot_name: str 15 | bot_secret: str 16 | mode: extra_types.Mode 17 | 18 | 19 | @dataclass 20 | class MatchStarted(MessageBase): 21 | num_rounds: int 22 | mode: extra_types.Mode 23 | map_size: extra_types.XY 24 | your_id: int 25 | view_radius: int 26 | mining_radius: int 27 | attack_radius: int 28 | move_time_limit: int 29 | match_id: int = 0 30 | num_bots: int = 0 31 | 32 | 33 | @dataclass 34 | class Update(MessageBase): 35 | round: int 36 | bot: list[extra_types.BotInfo] = field(default_factory=list) 37 | block: list[extra_types.XY] = field(default_factory=list) 38 | coin: list[extra_types.XY] = field(default_factory=list) 39 | 40 | 41 | @dataclass 42 | class Move(MessageBase): 43 | offset: extra_types.XY 44 | 45 | 46 | @dataclass 47 | class MatchOver(MessageBase): 48 | pass 49 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | ru.croccode.hypernull 7 | hypernull 8 | 1.0-SNAPSHOT 9 | pom 10 | 11 | common 12 | server 13 | player 14 | starter-bot 15 | map-generator 16 | maze-generator 17 | 18 | 19 | UTF-8 20 | 11 21 | 22 | 23 | 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-compiler-plugin 28 | 3.8.1 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/io/SocketSession.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.io; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStreamReader; 5 | import java.io.OutputStreamWriter; 6 | import java.io.Reader; 7 | import java.io.Writer; 8 | import java.net.Socket; 9 | import java.net.SocketException; 10 | 11 | import ru.croccode.hypernull.message.Message; 12 | import ru.croccode.hypernull.util.Check; 13 | 14 | public class SocketSession extends Session { 15 | 16 | private final Socket socket; 17 | 18 | public SocketSession(Socket socket) throws IOException { 19 | super(socketReader(socket), socketWriter(socket)); 20 | this.socket = socket; 21 | } 22 | 23 | private static Reader socketReader(Socket socket) throws IOException { 24 | Check.notNull(socket); 25 | return new InputStreamReader(socket.getInputStream()); 26 | } 27 | 28 | private static Writer socketWriter(Socket socket) throws IOException { 29 | Check.notNull(socket); 30 | return new OutputStreamWriter(socket.getOutputStream()); 31 | } 32 | 33 | public boolean isOpen() { 34 | return socket.isConnected() && !socket.isClosed(); 35 | } 36 | 37 | @Override 38 | public void close() throws IOException { 39 | if (!socket.isClosed()) 40 | socket.close(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/match/Bot.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.match; 2 | 3 | import ru.croccode.hypernull.geometry.Point; 4 | import ru.croccode.hypernull.util.Check; 5 | 6 | // bot state 7 | public class Bot { 8 | 9 | private final K key; 10 | 11 | private final String name; 12 | 13 | private boolean active; 14 | 15 | private Point position; 16 | 17 | private int numCoins; 18 | 19 | // package-private 20 | Bot(K key, String name, Point initPosition) { 21 | Check.notNull(key); 22 | 23 | this.key = key; 24 | this.name = name; 25 | this.active = true; 26 | this.position = initPosition; 27 | this.numCoins = 0; 28 | } 29 | 30 | public K getKey() { 31 | return key; 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public boolean isActive() { 39 | return active; 40 | } 41 | 42 | void deactivate() { 43 | active = false; 44 | } 45 | 46 | public Point getPosition() { 47 | return position; 48 | } 49 | 50 | void setPosition(Point position) { 51 | this.position = position; 52 | } 53 | 54 | public int getNumCoins() { 55 | return numCoins; 56 | } 57 | 58 | void addCoins(int numCoins) { 59 | this.numCoins += numCoins; 60 | } 61 | 62 | void resetCoins() { 63 | numCoins = 0; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /starter-bot-python/bot.py: -------------------------------------------------------------------------------- 1 | from client.bot_base import BotBase 2 | from client.message.extra_types import Mode 3 | from client.message.messages import MatchStarted, Update 4 | 5 | # Параметры регистрации бота на сервере 6 | bot_name = 'SuperStarter' 7 | bot_secret = '' 8 | mode = Mode.FRIENDLY 9 | 10 | 11 | class Bot(BotBase): 12 | # Старт 13 | def on_match_start(self, match_info: MatchStarted) -> None: 14 | # Правила матча 15 | self.match_info = match_info 16 | self.id = match_info.your_id 17 | print(match_info) 18 | print(f'Матч стартовал! Бот <{self.name}> готов') 19 | 20 | # Каждый ход 21 | def on_update(self, update: Update) -> tuple[int, int]: 22 | # Данные раунда: что бот "видит" 23 | round_number = update.round 24 | coins = update.coin 25 | blocks = update.block 26 | my_bot = next((bot for bot in update.bot if bot.id == self.id), None) 27 | opponents = [bot for bot in update.bot if bot.id != self.id] 28 | 29 | # Выбираем направление движения 30 | import random 31 | dx = random.choice([-1, 0, 1]) 32 | dy = random.choice([-1, 0, 1]) 33 | 34 | return dx, dy # Отправляем ход серверу 35 | 36 | # Конец матча 37 | def on_match_over(self) -> None: 38 | print('Матч окончен') 39 | -------------------------------------------------------------------------------- /starter-bot-python/client/socket_session.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import socket 3 | 4 | 5 | class SocketSession: 6 | _buffer_size = 8192 7 | _timeout_move = 'move\noffset 0 0\nend\n' 8 | 9 | def __init__(self, host: str, port: int): 10 | self.socket = socket.socket() 11 | self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 12 | self.socket.connect((host, port)) 13 | self.buffer = bytearray() 14 | 15 | def __del__(self): 16 | self.socket.close() 17 | 18 | def read(self) -> list[str]: 19 | end_index = self._find_end_index() 20 | while end_index == -1: 21 | self.buffer += self.socket.recv(self._buffer_size) 22 | end_index = self._find_end_index() 23 | 24 | data = self.buffer[:end_index + 3] 25 | self.buffer = self.buffer[end_index + 4:] 26 | 27 | if len(self.buffer) > 0: 28 | logging.warning('skipping round, seems like your bot had timed out') 29 | self.write(self._timeout_move) 30 | return self.read() 31 | 32 | return data.decode().split('\n') 33 | 34 | def _find_end_index(self) -> int: 35 | return self.buffer.find(b'end\n', len(self.buffer) - self._buffer_size - 4) 36 | 37 | def write(self, data: str) -> None: 38 | self.socket.sendall(data.encode()) 39 | -------------------------------------------------------------------------------- /player/src/main/java/ru/croccode/hypernull/player/model/MatchModel.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.player.model; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.util.ArrayDeque; 8 | import java.util.Collection; 9 | import java.util.Queue; 10 | 11 | import javafx.beans.property.ObjectProperty; 12 | import javafx.beans.property.SimpleObjectProperty; 13 | import ru.croccode.hypernull.player.MatchLogParser; 14 | 15 | public class MatchModel { 16 | 17 | // active match log 18 | private final ObjectProperty match = new SimpleObjectProperty<>(null); 19 | 20 | private final Queue queue = new ArrayDeque<>(); 21 | 22 | public ObjectProperty getMatch() { 23 | return match; 24 | } 25 | 26 | public void queueReplays(Collection paths) { 27 | if (paths == null || paths.isEmpty()) 28 | return; 29 | queue.clear(); 30 | queue.addAll(paths); 31 | advance(); 32 | } 33 | 34 | public void advance() { 35 | while (!queue.isEmpty()) { 36 | Path path = queue.poll(); 37 | try (InputStream in = Files.newInputStream(path)) { 38 | MatchLog log = MatchLogParser.parse(in); 39 | match.setValue(log); 40 | return; 41 | } catch (IOException e) { 42 | e.printStackTrace(); 43 | // try next path 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/message/Update.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.message; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | import ru.croccode.hypernull.geometry.Point; 7 | 8 | public class Update extends Message { 9 | 10 | private Integer round; 11 | 12 | // bot positions; key - bot id 13 | private Map bots; 14 | 15 | // bot coins; key - bot id 16 | private Map botCoins; 17 | 18 | // blocked map positions 19 | private Set blocks; 20 | 21 | // coin positions 22 | private Set coins; 23 | 24 | public Integer getRound() { 25 | return round; 26 | } 27 | 28 | public void setRound(Integer round) { 29 | this.round = round; 30 | } 31 | 32 | public Map getBots() { 33 | return bots; 34 | } 35 | 36 | public void setBots(Map bots) { 37 | this.bots = bots; 38 | } 39 | 40 | public Map getBotCoins() { 41 | return botCoins; 42 | } 43 | 44 | public void setBotCoins(Map botCoins) { 45 | this.botCoins = botCoins; 46 | } 47 | 48 | public Set getBlocks() { 49 | return blocks; 50 | } 51 | 52 | public void setBlocks(Set blocks) { 53 | this.blocks = blocks; 54 | } 55 | 56 | public Set getCoins() { 57 | return coins; 58 | } 59 | 60 | public void setCoins(Set coins) { 61 | this.coins = coins; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/util/Check.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.util; 2 | 3 | import java.util.Collection; 4 | 5 | public final class Check { 6 | 7 | private Check() { 8 | } 9 | 10 | public static T notNull(T reference) { 11 | return notNull(reference, null); 12 | } 13 | 14 | public static T notNull(T reference, String message) { 15 | if (reference == null) { 16 | throw new NullPointerException(message != null 17 | ? message 18 | : "Object reference is null"); 19 | } 20 | return reference; 21 | } 22 | 23 | public static String notEmpty(String str) { 24 | return notEmpty(str, null); 25 | } 26 | 27 | public static String notEmpty(String str, String message) { 28 | if (str == null || str.isEmpty()) { 29 | throw new IllegalArgumentException(message != null 30 | ? message 31 | : "String is empty"); 32 | } 33 | return str; 34 | } 35 | 36 | public static Collection notEmpty(Collection collection) { 37 | notNull(collection); 38 | condition(!collection.isEmpty()); 39 | return collection; 40 | } 41 | 42 | public static void condition(boolean condition) { 43 | condition(condition, null); 44 | } 45 | 46 | public static void condition(boolean condition, String message) { 47 | if (!condition) { 48 | throw new IllegalArgumentException(message != null 49 | ? message 50 | : "Condition violated"); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /map-generator/src/main/java/MapGenerator/Saver.java: -------------------------------------------------------------------------------- 1 | package MapGenerator; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.FileWriter; 5 | import java.io.IOException; 6 | import java.util.ArrayList; 7 | 8 | public class Saver { 9 | private static int id=1; 10 | 11 | public static void save(Map map) throws IOException { 12 | try(BufferedWriter writer = new BufferedWriter(new FileWriter("map_2_"+String.valueOf(id)+".map"))) { 13 | 14 | writer.write("map_size " + map.getHeight() + " " + map.getWidth()); 15 | writer.newLine(); 16 | int viewRadius = Math.min(map.getHeight(),map.getWidth())/7; 17 | writer.write("view_radius "+ viewRadius); 18 | writer.newLine(); 19 | writer.write("mining_radius "+ (viewRadius-1)); 20 | writer.newLine(); 21 | writer.write("attack_radius "+ (viewRadius-2)); 22 | writer.newLine(); 23 | 24 | for(int h = map.getHeight()-1; h>=0; h--) { 25 | for (int w = 0; w < map.getWidth(); w++) { 26 | if(map.get(h,w) == Parameter.WALL) { 27 | writer.write("block "+h+" "+w); 28 | writer.newLine(); 29 | } 30 | } 31 | } 32 | 33 | for(ArrayList point : map.getSpawnpoints()) { 34 | writer.write("spawn_position "+point.get(0)+" "+point.get(1)); 35 | writer.newLine(); 36 | } 37 | 38 | id++; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/map/MapWriter.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.map; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | import java.io.Writer; 6 | 7 | import ru.croccode.hypernull.domain.MatchMap; 8 | import ru.croccode.hypernull.geometry.Point; 9 | import ru.croccode.hypernull.util.Check; 10 | 11 | public class MapWriter implements Closeable { 12 | 13 | private final Writer w; 14 | 15 | public MapWriter(Writer w) { 16 | Check.notNull(w); 17 | this.w = w; 18 | } 19 | 20 | public void write(MatchMap map) throws IOException { 21 | Check.notNull(map); 22 | writeParameters("map_size", map.getWidth(), map.getHeight()); 23 | writeParameters("view_radius", map.getViewRadius()); 24 | writeParameters("mining_radius", map.getMiningRadius()); 25 | writeParameters("attack_radius", map.getAttackRadius()); 26 | for (int y = 0; y < map.getHeight(); y++) { 27 | for (int x = 0; x < map.getWidth(); x++) { 28 | if (map.isBlocked(x, y)) { 29 | writeParameters("block", x, y); 30 | } 31 | } 32 | } 33 | for (Point point : map.getSpawnPositions()) { 34 | writeParameters("spawn_position", point.x(), point.y()); 35 | } 36 | } 37 | 38 | private void writeParameters(String name, Object... values) throws IOException { 39 | Check.notEmpty(name); 40 | w.write(name); 41 | if (values != null) { 42 | for (Object value : values) { 43 | w.write(" "); 44 | w.write(String.valueOf(value)); 45 | } 46 | } 47 | w.write("\n"); 48 | } 49 | 50 | @Override 51 | public void close() throws IOException { 52 | w.close(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /player/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ru.croccode.hypernull 8 | hypernull 9 | 1.0-SNAPSHOT 10 | 11 | player 12 | 1.0-SNAPSHOT 13 | 14 | 15 | ru.croccode.hypernull 16 | common 17 | 1.0-SNAPSHOT 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-assembly-plugin 25 | 3.3.0 26 | 27 | 28 | assembly 29 | prepare-package 30 | 31 | single 32 | 33 | 34 | 35 | 36 | 37 | 38 | ru.croccode.hypernull.player.App 39 | 40 | 41 | 42 | jar-with-dependencies 43 | 44 | ${project.artifactId} 45 | false 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ru.croccode.hypernull 8 | hypernull 9 | 1.0-SNAPSHOT 10 | 11 | server 12 | 1.0-SNAPSHOT 13 | 14 | 15 | ru.croccode.hypernull 16 | common 17 | 1.0-SNAPSHOT 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-assembly-plugin 25 | 3.3.0 26 | 27 | 28 | assembly 29 | prepare-package 30 | 31 | single 32 | 33 | 34 | 35 | 36 | 37 | 38 | ru.croccode.hypernull.server.HyperNull 39 | 40 | 41 | 42 | jar-with-dependencies 43 | 44 | ${project.artifactId} 45 | false 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /starter-bot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | ru.croccode.hypernull 8 | hypernull 9 | 1.0-SNAPSHOT 10 | 11 | starter-bot 12 | 1.0-SNAPSHOT 13 | 14 | 15 | ru.croccode.hypernull 16 | common 17 | 1.0-SNAPSHOT 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-assembly-plugin 25 | 3.3.0 26 | 27 | 28 | assembly 29 | prepare-package 30 | 31 | single 32 | 33 | 34 | 35 | 36 | 37 | 38 | ru.croccode.hypernull.bot.StarterBot 39 | 40 | 41 | 42 | jar-with-dependencies 43 | 44 | ${project.artifactId} 45 | false 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /starter-bot-python/client/client.py: -------------------------------------------------------------------------------- 1 | from .bot_base import BotImpl 2 | from .socket_session import SocketSession 3 | from .message import factory, messages, extra_types 4 | 5 | 6 | class HypernullClient: 7 | version: int = 1 8 | 9 | def __init__(self, host: str = 'localhost', port: int = 2021): 10 | self.session = SocketSession(host, port) 11 | msg = self.get() 12 | if not isinstance(msg, messages.Hello): 13 | raise Exception( 14 | f'Wanted message {messages.Hello.__name__}, got: {type(msg)}' 15 | ) 16 | 17 | if msg.protocol_version != self.version: 18 | raise Exception( 19 | f'Client v{self.version}, but Server v{msg.protocol_version}' 20 | ) 21 | 22 | def get(self) -> factory.Message: 23 | data = self.session.read() 24 | return factory.MessageFactory.load(data) 25 | 26 | def send(self, msg: messages.MessageBase) -> None: 27 | data = msg.dump() 28 | self.session.write(data) 29 | 30 | def register(self, bot: BotImpl) -> None: 31 | register = messages.Register( 32 | bot_name=bot.name, 33 | bot_secret=bot.secret, 34 | mode=bot.mode, 35 | ) 36 | self.send(register) 37 | 38 | def get_update(self) -> messages.Update | None: 39 | update: messages.Update | messages.MatchOver = self.get() 40 | if isinstance(update, messages.MatchOver): 41 | return None 42 | return update 43 | 44 | def move(self, dx: int, dy: int) -> None: 45 | move = messages.Move( 46 | offset=extra_types.XY(dx, dy) 47 | ) 48 | self.send(move) 49 | -------------------------------------------------------------------------------- /maze-generator/src/main/java/InitSettings.java: -------------------------------------------------------------------------------- 1 | import java.util.List; 2 | 3 | public class InitSettings { 4 | private int WIDTH; 5 | private int HEIGHT; 6 | private int CountBot; 7 | private List ListOfSpawn; 8 | 9 | public InitSettings(int WIDTH, int HEIGHT, int countBot, List listOfSpawn) { 10 | this.WIDTH = WIDTH; 11 | this.HEIGHT = HEIGHT; 12 | this.CountBot = countBot; 13 | this.ListOfSpawn = listOfSpawn; 14 | } 15 | 16 | 17 | //задаем радиус видимости в зависимости от количества ботов 18 | public int getViewRadius() { 19 | if ( CountBot <= 4) { 20 | return 4; 21 | } else if (CountBot <= 8) { 22 | return 7; 23 | } else if (CountBot <= 12) { 24 | return 10; 25 | }else { 26 | return 13; 27 | } 28 | } 29 | 30 | //задаем радиус майнинга монет в зависимости от количества ботов 31 | public int getMiningRadius() { 32 | if ( CountBot <= 4) { 33 | return 2; 34 | } else if (CountBot <= 8) { 35 | return 3; 36 | } else { 37 | return 5; 38 | } 39 | } 40 | //задаем радиус атаки в зависимости от количества ботов 41 | public int getAtackRadius() { 42 | if ( CountBot <= 4) { 43 | return 2; 44 | } else if (CountBot <= 8) { 45 | return 3; 46 | } else { 47 | return 5; 48 | } 49 | } 50 | 51 | public int getWIDTH() { 52 | return WIDTH; 53 | } 54 | 55 | public int getHEIGHT() { 56 | return HEIGHT; 57 | } 58 | public List getListOfSpawn(){ 59 | return ListOfSpawn; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /maze-generator/src/main/java/WriteMap.java: -------------------------------------------------------------------------------- 1 | import java.io.FileWriter; 2 | import java.io.IOException; 3 | import java.io.Writer; 4 | import java.util.List; 5 | 6 | public class WriteMap { 7 | private boolean[][] MAP; 8 | private int CountBot; 9 | private List ListOfSpawn; 10 | 11 | public WriteMap(boolean[][] MAP,int countBot,List listOfSpawn) { 12 | this.MAP = MAP; 13 | CountBot=countBot; 14 | ListOfSpawn=listOfSpawn; 15 | } 16 | 17 | 18 | public void writeMap() throws IOException { 19 | InitSettings settings=new InitSettings(MAP.length,MAP[0].length,CountBot,ListOfSpawn); 20 | String nameOfFile="maze_"+CountBot+"_6.map"; 21 | Writer writerOnTxt=new FileWriter("/Users/alekseyzhizhin/Documents/GitHub/hypernull/CreateMap/src/main/java/maze/"+nameOfFile); 22 | writerOnTxt.write("map_size "+settings.getWIDTH()+" "+settings.getHEIGHT()+"\n"); 23 | writerOnTxt.write("view_radius "+settings.getViewRadius()+"\n"); 24 | writerOnTxt.write("mining_radius "+settings.getMiningRadius()+"\n"); 25 | writerOnTxt.write("attack_radius "+settings.getAtackRadius()+"\n"); 26 | 27 | for (int i = 0; i < MAP.length; i++) { 28 | for (int j = 0; j < MAP[0].length; j++) { 29 | if (MAP[i][j]) { 30 | writerOnTxt.write("Block " + i + " " + j + "\n"); 31 | }else { 32 | } 33 | } 34 | } 35 | for (int i=0;i> maps = new HashMap<>(); 22 | 23 | @Override 24 | public MatchMap randomMap(int numBots) { 25 | Check.condition(numBots > 0); 26 | List keys = maps.keySet() 27 | .stream() 28 | .filter(k -> k >= numBots) 29 | .collect(Collectors.toList()); 30 | if (keys.isEmpty()) 31 | return null; 32 | int rndKey = keys.get(rnd.nextInt(keys.size())); 33 | List values = maps.get(rndKey); 34 | return values.get(rnd.nextInt(values.size())); 35 | } 36 | 37 | private void add(MatchMap map) { 38 | Check.notNull(map); 39 | int numBots = map.getSpawnPositions().size(); 40 | maps.computeIfAbsent(numBots, k -> new ArrayList<>()) 41 | .add(map); 42 | } 43 | 44 | public static MapStore load(Path path) throws IOException { 45 | Check.notNull(path); 46 | MapStore store = new MapStore(); 47 | if (Files.exists(path)) { 48 | Files.walk(path) 49 | .filter(Files::isRegularFile) 50 | .filter(p -> p.toString().toLowerCase().endsWith(".map")) 51 | .forEach(p -> { 52 | try (MapReader r = new MapReader(Files.newBufferedReader(p))) { 53 | System.out.println("Loading map " + p); 54 | store.add(r.readMap()); 55 | } catch (IOException e) { 56 | throw new RuntimeException(e); 57 | } 58 | }); 59 | } 60 | return store; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /starter-bot/src/main/java/ru/croccode/hypernull/bot/BotConfig.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.bot; 2 | 3 | import ru.croccode.hypernull.domain.MatchMode; 4 | import ru.croccode.hypernull.util.Check; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.util.Properties; 11 | 12 | public class BotConfig { 13 | 14 | private final String serverHost; 15 | 16 | private final int serverPort; 17 | 18 | private final String botName; 19 | 20 | private final String botSecret; 21 | 22 | private final MatchMode mode; 23 | 24 | public BotConfig(Properties properties) { 25 | Check.notNull(properties); 26 | 27 | serverHost = properties.getProperty( 28 | "server.host", "localhost"); 29 | serverPort = Integer.parseInt(properties.getProperty( 30 | "server.port", "2021")); 31 | botName = properties.getProperty( 32 | "bot.name", "starter-bot"); 33 | botSecret = properties.getProperty( 34 | "bot.secret"); 35 | mode = MatchMode.valueOf(properties.getProperty( 36 | "mode", "FRIENDLY")); 37 | } 38 | 39 | public static BotConfig load(Path path) throws IOException { 40 | Properties properties = new Properties(); 41 | if (path != null && Files.exists(path)) { 42 | try (InputStream in = Files.newInputStream(path)) { 43 | properties.load(in); 44 | } 45 | } 46 | return new BotConfig(properties); 47 | } 48 | 49 | public String getServerHost() { 50 | return serverHost; 51 | } 52 | 53 | public int getServerPort() { 54 | return serverPort; 55 | } 56 | 57 | public String getBotName() { 58 | return botName; 59 | } 60 | 61 | public String getBotSecret() { 62 | return botSecret; 63 | } 64 | 65 | public MatchMode getMode() { 66 | return mode; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /starter-bot/src/main/java/ru/croccode/hypernull/bot/BotMatchRunner.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.bot; 2 | 3 | import java.io.IOException; 4 | 5 | import ru.croccode.hypernull.geometry.Offset; 6 | import ru.croccode.hypernull.io.SocketSession; 7 | import ru.croccode.hypernull.message.Hello; 8 | import ru.croccode.hypernull.message.MatchOver; 9 | import ru.croccode.hypernull.message.MatchStarted; 10 | import ru.croccode.hypernull.message.Message; 11 | import ru.croccode.hypernull.message.Move; 12 | import ru.croccode.hypernull.message.Register; 13 | import ru.croccode.hypernull.message.Update; 14 | import ru.croccode.hypernull.util.Check; 15 | 16 | public class BotMatchRunner implements Runnable { 17 | 18 | private final Bot bot; 19 | 20 | private final SocketSession session; 21 | 22 | public BotMatchRunner(Bot bot, SocketSession session) { 23 | Check.notNull(bot); 24 | Check.notNull(session); 25 | 26 | this.bot = bot; 27 | this.session = session; 28 | } 29 | 30 | @Override 31 | public void run() { 32 | try { 33 | runImpl(); 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | try { 37 | session.close(); 38 | } catch (IOException ignore) { 39 | } 40 | } 41 | } 42 | 43 | public void runImpl() throws IOException { 44 | // wait for a server hello 45 | Hello hello = session.read(Hello.class); 46 | // send register 47 | Register register = bot.onHello(hello); 48 | session.write(register); 49 | // wait for a match start 50 | MatchStarted matchStarted = session.read(MatchStarted.class); 51 | bot.onMatchStarted(matchStarted); 52 | // wait for update 53 | while (true) { 54 | Message message = session.read(); 55 | if (message instanceof MatchOver) { 56 | bot.onMatchOver((MatchOver)message); 57 | session.close(); 58 | break; 59 | } 60 | if (message instanceof Update) { 61 | Move move = bot.onUpdate((Update)message); 62 | if (move == null) { 63 | move = new Move(); 64 | move.setOffset(new Offset(0, 0)); 65 | } 66 | session.write(move); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /starter-bot-python/client/message/factory.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | import inspect 3 | import types 4 | from collections import defaultdict 5 | from typing import TypeVar, Type 6 | 7 | from . import messages 8 | 9 | 10 | Message = TypeVar('Message', bound=messages.MessageBase) 11 | 12 | 13 | class MessageFactory: 14 | _known_message_types: dict[str, Type[Message]] = dict( 15 | inspect.getmembers(messages, inspect.isclass) 16 | ) 17 | 18 | @classmethod 19 | def load(cls, data: list[str]) -> Message: 20 | if not data: 21 | raise Exception('got empty data') 22 | 23 | command = cls._to_camel_case(data[0]) 24 | if command not in cls._known_message_types: 25 | raise Exception(f'unknown command: {command}') 26 | 27 | message_class = cls._known_message_types[command] 28 | field_type_mapping: dict[str, tuple[type | None, type]] = { 29 | field.name: cls._get_field_types(field) 30 | for field in dataclasses.fields(message_class) 31 | } 32 | 33 | params = defaultdict(list) 34 | for row in data[1:-1]: 35 | name, *value = row.split() 36 | 37 | container, real_type = field_type_mapping[name] 38 | if container is None: 39 | params[name] = real_type(*value) 40 | elif container is list: 41 | params[name].append(real_type(*value)) 42 | else: 43 | raise Exception(f'cannot handle {command}:{name}:{container}') 44 | 45 | return message_class(**params) 46 | 47 | @staticmethod 48 | def _get_field_types(field: dataclasses.Field) -> tuple[type | None, type]: 49 | if isinstance(field.type, types.GenericAlias): 50 | container = field.type.__origin__ 51 | real_type = field.type.__args__[0] 52 | else: 53 | container = None 54 | real_type = field.type 55 | return container, real_type 56 | 57 | @staticmethod 58 | def _to_camel_case(snake_case: str) -> str: 59 | return ''.join(t.title() for t in snake_case.split('_')) 60 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/match/MapIndex.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.match; 2 | 3 | import java.util.HashSet; 4 | import java.util.List; 5 | import java.util.Set; 6 | 7 | import ru.croccode.hypernull.domain.MatchMap; 8 | import ru.croccode.hypernull.geometry.KdTree; 9 | import ru.croccode.hypernull.geometry.Point; 10 | import ru.croccode.hypernull.util.Check; 11 | 12 | public class MapIndex { 13 | 14 | private final KdTree blocked; 15 | private final KdTree free; 16 | 17 | public MapIndex(MatchMap map) { 18 | Check.notNull(map); 19 | 20 | KdTree.Builder blockedBuilder = KdTree.builder(); 21 | KdTree.Builder freeBuilder = KdTree.builder(); 22 | 23 | int w = map.getWidth(); 24 | int h = map.getHeight(); 25 | for (int x = 0; x < w; x++) { 26 | for (int y = 0; y < h; y++) { 27 | KdTree.Builder target = map.isBlocked(x, y) 28 | ? blockedBuilder 29 | : freeBuilder; 30 | Point value = new Point(x, y); 31 | target.add(new Point(x, y), value); 32 | 33 | // reflections of the 34 | // map quadrants: 35 | // 4 3 4 3 36 | // 1 2 1 2 37 | // 4 3 4 3 38 | // 1 2 1 2 39 | 40 | int dx = x < w / 2 ? w : -w; 41 | int dy = y < h / 2 ? h : -h; 42 | target.add(new Point(x + dx, y), value); 43 | target.add(new Point(x, y + dy), value); 44 | target.add(new Point(x + dx, y + dy), value); 45 | } 46 | } 47 | 48 | this.blocked = blockedBuilder.build(); 49 | this.free = freeBuilder.build(); 50 | } 51 | 52 | public Point nearestFree(Point point, Set exclude) { 53 | Check.notNull(point); 54 | KdTree.Neighbor nearest = free.nearestNeighbor(point, exclude); 55 | return nearest != null 56 | ? nearest.value() 57 | : null; // no free cells 58 | } 59 | 60 | public Set blockedInRange(Point point, int radius) { 61 | Check.notNull(point); 62 | List> nodes = blocked.neighborsInRange(point, radius); 63 | Set points = new HashSet<>(nodes.size()); 64 | for (KdTree.Neighbor node : nodes) 65 | points.add(node.value()); 66 | return points; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/io/Session.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.io; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.BufferedWriter; 5 | import java.io.Closeable; 6 | import java.io.IOException; 7 | import java.io.Reader; 8 | import java.io.Writer; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Objects; 12 | 13 | import ru.croccode.hypernull.message.Message; 14 | import ru.croccode.hypernull.message.Messages; 15 | import ru.croccode.hypernull.util.Check; 16 | 17 | public class Session implements Closeable { 18 | 19 | private final BufferedReader reader; 20 | 21 | private final BufferedWriter writer; 22 | 23 | private volatile boolean closed; 24 | 25 | public Session(Reader reader, Writer writer) { 26 | Check.notNull(reader); 27 | Check.notNull(writer); 28 | 29 | this.reader = reader instanceof BufferedReader 30 | ? (BufferedReader)reader 31 | : new BufferedReader(reader); 32 | this.writer = writer instanceof BufferedWriter 33 | ? (BufferedWriter)writer 34 | : new BufferedWriter(writer); 35 | } 36 | 37 | public void write(Message message) throws IOException { 38 | if (message == null) 39 | return; 40 | List lines = Messages.format(message); 41 | for (String line : lines) { 42 | writer.write(line); 43 | writer.write("\n"); 44 | } 45 | writer.flush(); 46 | } 47 | 48 | public T read(Class type) throws IOException { 49 | Check.notNull(type); 50 | 51 | while (true) { 52 | Message message = read(); 53 | if (message == null) 54 | return null; // end of stream 55 | if (message.getClass() == type) 56 | return (T)message; 57 | } 58 | } 59 | 60 | public Message read() throws IOException { 61 | List lines = new ArrayList<>(); 62 | while (true) { 63 | String line = reader.readLine(); 64 | if (line == null) 65 | return null; 66 | if (line.isEmpty()) 67 | continue; 68 | lines.add(line); 69 | if (Objects.equals(line.trim(), "end")) { 70 | return Messages.parse(lines); 71 | } 72 | } 73 | } 74 | 75 | @Override 76 | public void close() throws IOException { 77 | writer.close(); 78 | reader.close(); 79 | closed = true; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/message/MatchStarted.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.message; 2 | 3 | import ru.croccode.hypernull.geometry.Size; 4 | import ru.croccode.hypernull.domain.MatchMode; 5 | 6 | public class MatchStarted extends Message { 7 | 8 | private String matchId; 9 | 10 | private Integer numRounds; 11 | 12 | private MatchMode mode; 13 | 14 | private Size mapSize; 15 | 16 | private Integer numBots; 17 | 18 | private Integer yourId; 19 | 20 | private Integer viewRadius; 21 | 22 | private Integer miningRadius; 23 | 24 | private Integer attackRadius; 25 | 26 | private Long moveTimeLimit; 27 | 28 | public String getMatchId() { 29 | return matchId; 30 | } 31 | 32 | public void setMatchId(String matchId) { 33 | this.matchId = matchId; 34 | } 35 | 36 | public Integer getNumRounds() { 37 | return numRounds; 38 | } 39 | 40 | public void setNumRounds(Integer numRounds) { 41 | this.numRounds = numRounds; 42 | } 43 | 44 | public MatchMode getMode() { 45 | return mode; 46 | } 47 | 48 | public void setMode(MatchMode mode) { 49 | this.mode = mode; 50 | } 51 | 52 | public Size getMapSize() { 53 | return mapSize; 54 | } 55 | 56 | public void setMapSize(Size mapSize) { 57 | this.mapSize = mapSize; 58 | } 59 | 60 | public Integer getNumBots() { 61 | return numBots; 62 | } 63 | 64 | public void setNumBots(Integer numBots) { 65 | this.numBots = numBots; 66 | } 67 | 68 | public Integer getYourId() { 69 | return yourId; 70 | } 71 | 72 | public void setYourId(Integer yourId) { 73 | this.yourId = yourId; 74 | } 75 | 76 | public Integer getViewRadius() { 77 | return viewRadius; 78 | } 79 | 80 | public void setViewRadius(Integer viewRadius) { 81 | this.viewRadius = viewRadius; 82 | } 83 | 84 | public Integer getMiningRadius() { 85 | return miningRadius; 86 | } 87 | 88 | public void setMiningRadius(Integer miningRadius) { 89 | this.miningRadius = miningRadius; 90 | } 91 | 92 | public Integer getAttackRadius() { 93 | return attackRadius; 94 | } 95 | 96 | public void setAttackRadius(Integer attackRadius) { 97 | this.attackRadius = attackRadius; 98 | } 99 | 100 | public Long getMoveTimeLimit() { 101 | return moveTimeLimit; 102 | } 103 | 104 | public void setMoveTimeLimit(Long moveTimeLimit) { 105 | this.moveTimeLimit = moveTimeLimit; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /starter-bot/src/main/java/ru/croccode/hypernull/bot/StarterBot.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.bot; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.Socket; 6 | import java.nio.file.Paths; 7 | import java.util.Random; 8 | 9 | import ru.croccode.hypernull.domain.MatchMode; 10 | import ru.croccode.hypernull.geometry.Offset; 11 | import ru.croccode.hypernull.io.SocketSession; 12 | import ru.croccode.hypernull.message.Hello; 13 | import ru.croccode.hypernull.message.MatchOver; 14 | import ru.croccode.hypernull.message.MatchStarted; 15 | import ru.croccode.hypernull.message.Move; 16 | import ru.croccode.hypernull.message.Register; 17 | import ru.croccode.hypernull.message.Update; 18 | 19 | public class StarterBot implements Bot { 20 | 21 | private static final Random rnd = new Random(System.currentTimeMillis()); 22 | 23 | private final String name; 24 | 25 | private final MatchMode mode; 26 | 27 | private Offset moveOffset; 28 | 29 | private int moveCounter = 0; 30 | 31 | public StarterBot(String name, MatchMode mode) { 32 | this.name = name; 33 | this.mode = mode; 34 | } 35 | 36 | @Override 37 | public Register onHello(Hello hello) { 38 | Register register = new Register(); 39 | register.setBotName(name); 40 | register.setMode(mode); 41 | return register; 42 | } 43 | 44 | @Override 45 | public void onMatchStarted(MatchStarted matchStarted) { 46 | } 47 | 48 | @Override 49 | public Move onUpdate(Update update) { 50 | if (moveOffset == null || moveCounter > 5 + rnd.nextInt(5)) { 51 | moveOffset = new Offset( 52 | rnd.nextInt(3) - 1, 53 | rnd.nextInt(3) - 1 54 | ); 55 | moveCounter = 0; 56 | } 57 | moveCounter++; 58 | Move move = new Move(); 59 | move.setOffset(moveOffset); 60 | return move; 61 | } 62 | 63 | @Override 64 | public void onMatchOver(MatchOver matchOver) { 65 | } 66 | 67 | public static void main(String[] args) throws IOException { 68 | String configPath = args.length > 0 69 | ? args[0] 70 | : "bot.properties"; 71 | BotConfig botConfig = BotConfig.load(Paths.get(configPath)); 72 | 73 | Socket socket = new Socket(); 74 | socket.setTcpNoDelay(true); 75 | socket.setSoTimeout(300_000); 76 | socket.connect(new InetSocketAddress( 77 | botConfig.getServerHost(), 78 | botConfig.getServerPort())); 79 | 80 | SocketSession session = new SocketSession(socket); 81 | StarterBot bot = new StarterBot(botConfig.getBotName(), botConfig.getMode()); 82 | new BotMatchRunner(bot, session).run(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /player/src/main/java/ru/croccode/hypernull/player/Styles.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.player; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import javafx.geometry.Insets; 7 | import javafx.scene.layout.Background; 8 | import javafx.scene.layout.BackgroundFill; 9 | import javafx.scene.layout.Border; 10 | import javafx.scene.layout.BorderStroke; 11 | import javafx.scene.layout.BorderStrokeStyle; 12 | import javafx.scene.layout.BorderWidths; 13 | import javafx.scene.layout.CornerRadii; 14 | import javafx.scene.paint.Color; 15 | import javafx.scene.text.Font; 16 | import javafx.scene.text.FontPosture; 17 | import javafx.scene.text.FontWeight; 18 | 19 | public final class Styles { 20 | 21 | private static final List BOT_COLORS = Arrays.asList( 22 | Color.rgb(246, 81, 113), 23 | Color.rgb(130, 237, 78), 24 | Color.rgb(82, 132, 211), 25 | Color.rgb(240, 7, 53), 26 | Color.rgb(78, 226, 7), 27 | Color.rgb(22, 84, 183)); 28 | 29 | public static final Color CLEAR_COLOR = Color.rgb(33, 33, 33); 30 | public static final Color TEXT_COLOR = Color.rgb(200, 200, 200); 31 | public static final Color COIN_COLOR = Color.rgb(255, 180, 49); 32 | public static final Color COLLECTED_COIN_COLOR = Color.rgb(255, 207, 122); 33 | public static final Color BLOCK_COLOR = Color.rgb(78, 155, 143); 34 | 35 | public static final double PADDING_HORIZONTAL = 12; 36 | public static final double PADDING_VERTICAL = 12; 37 | public static final double CANVAS_PADDING = 12; 38 | 39 | public static final Font DEFAULT_FONT = Font.font("Arial", FontPosture.REGULAR, 12); 40 | public static final Font INFO_FONT = Font.font("Arial", FontPosture.REGULAR, 14); 41 | public static final Font WEBDINGS_FONT = Font.font("Webdings", FontPosture.REGULAR, 24); 42 | 43 | private Styles() { 44 | } 45 | 46 | public static Color botColor(int botId) { 47 | return BOT_COLORS.get(botId % BOT_COLORS.size()); 48 | } 49 | 50 | public static BorderStroke lineStroke(Color color) { 51 | return new BorderStroke( 52 | color, 53 | BorderStrokeStyle.SOLID, 54 | CornerRadii.EMPTY, 55 | BorderWidths.DEFAULT); 56 | } 57 | 58 | public static Border lineBorder(Color color) { 59 | return new Border(lineStroke(color)); 60 | } 61 | 62 | public static BackgroundFill solidFill(Color color) { 63 | return new BackgroundFill( 64 | color, 65 | CornerRadii.EMPTY, 66 | Insets.EMPTY); 67 | } 68 | 69 | public static Background solidBackground(Color color) { 70 | return new Background(solidFill(color)); 71 | } 72 | 73 | public static Insets defaultPadding() { 74 | return new Insets( 75 | PADDING_VERTICAL, 76 | PADDING_HORIZONTAL, 77 | PADDING_VERTICAL, 78 | PADDING_HORIZONTAL); 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /player/src/main/java/ru/croccode/hypernull/player/controller/PlaybackController.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.player.controller; 2 | 3 | import java.util.concurrent.ScheduledFuture; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import javafx.application.Platform; 7 | import ru.croccode.hypernull.player.App; 8 | import ru.croccode.hypernull.player.model.MatchLog; 9 | import ru.croccode.hypernull.player.model.MatchModel; 10 | import ru.croccode.hypernull.player.model.PlaybackModel; 11 | import ru.croccode.hypernull.util.Check; 12 | 13 | public class PlaybackController { 14 | 15 | private final static long NEXT_ROUND_PERIOD_MILLIS = 200; 16 | 17 | private final MatchModel matchModel; 18 | 19 | private final PlaybackModel playbackModel; 20 | 21 | private ScheduledFuture scheduledNextRound; 22 | 23 | public PlaybackController(MatchModel matchModel, PlaybackModel playbackModel) { 24 | Check.notNull(matchModel); 25 | Check.notNull(playbackModel); 26 | this.matchModel = matchModel; 27 | this.playbackModel = playbackModel; 28 | 29 | matchModel.getMatch().addListener((observable, oldValue, newValue) 30 | -> Platform.runLater(this::onMatchChanged)); 31 | playbackModel.getPaused().addListener((observable, oldValue, newValue) 32 | -> Platform.runLater(this::onPausedChanged)); 33 | onMatchChanged(); 34 | onPausedChanged(); 35 | } 36 | 37 | private void onMatchChanged() { 38 | MatchLog match = matchModel.getMatch().get(); 39 | if (match == null) { 40 | playbackModel.getNumRounds().set(0); 41 | playbackModel.getRound().set(0); 42 | playbackModel.getPaused().set(true); 43 | } else { 44 | playbackModel.getNumRounds().set(match.getNumRounds()); 45 | playbackModel.getRound().set(0); 46 | playbackModel.getPaused().set(false); 47 | } 48 | } 49 | 50 | private void onPausedChanged() { 51 | boolean paused = playbackModel.getPaused().get(); 52 | if (!paused) { 53 | if (scheduledNextRound != null) 54 | scheduledNextRound.cancel(true); 55 | scheduledNextRound = App.SCHEDULER.scheduleAtFixedRate( 56 | this::nextRound, 57 | 0L, 58 | NEXT_ROUND_PERIOD_MILLIS, 59 | TimeUnit.MILLISECONDS); 60 | } else { 61 | if (scheduledNextRound != null) { 62 | scheduledNextRound.cancel(true); 63 | scheduledNextRound = null; 64 | } 65 | } 66 | } 67 | 68 | private void nextRound() { 69 | int round = playbackModel.getRound().get(); 70 | int numRounds = playbackModel.getNumRounds().get(); 71 | if (round >= numRounds) { 72 | playbackModel.getRound().set(numRounds); 73 | playbackModel.getPaused().set(true); 74 | // proceed to a next replay 75 | matchModel.advance(); 76 | } else { 77 | playbackModel.getRound().set(round + 1); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /map-generator/src/main/java/MapGenerator/Map.java: -------------------------------------------------------------------------------- 1 | package MapGenerator; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class Map { 6 | private Boolean[][] body; 7 | private ArrayList> spawnpoints = new ArrayList >(); 8 | private int height; 9 | private int width; 10 | 11 | Map(int height, int width) { 12 | this.height = height; 13 | this.width = width; 14 | body = new Boolean[height][width]; 15 | for(int h=0; h> getSpawnpoints() { 46 | return this.spawnpoints; 47 | } 48 | 49 | public void setSpawnpoint(int y, int x) { 50 | ArrayList point = new ArrayList<>(); 51 | point.add(y); 52 | point.add(x); 53 | spawnpoints.add(point); 54 | } 55 | 56 | public void print() { 57 | for(int h=height-1; h>=0; h--) {; 58 | for (int w = 0; w < width; w++) { 59 | boolean notSpawnpoint = true; 60 | for(int i=0; i= height) Y = y+dy-height; 21 | if(x+dx < 0) X = width+x+dx; 22 | if(x+dx >= width) X = x+dx-width; 23 | 24 | difference += checkNeighbour(map, Y,X); 25 | } 26 | } 27 | if(difference >= -1 ) 28 | map.set(y, x, Parameter.WALL); 29 | else 30 | map.set(y, x, Parameter.FLOOR); 31 | } 32 | 33 | public int checkNeighbour(Map map, int y, int x) { 34 | if(map.get(y,x) == Parameter.FLOOR) 35 | return -1; 36 | if(map.get(y,x) == Parameter.WALL) 37 | return +1; 38 | else return 0; 39 | } 40 | 41 | public void mirrorCell(Map map, int h, int w) { 42 | Boolean value = map.get(h, w); 43 | map.set(map.getHeight() - 1 - h, w, value); 44 | map.set(h, map.getWidth() - 1 - w, value); 45 | map.set(map.getHeight() - 1 - h, map.getWidth() - 1 - w, value); 46 | } 47 | 48 | @Override 49 | public Map generateMap(int originalHeight, int originalWidth) { 50 | Map resultMap = new Map(originalHeight, originalWidth); 51 | 52 | int height = originalHeight/2 + originalHeight%2; 53 | int width = originalWidth/2 + originalWidth%2; 54 | 55 | for(int k=0; k< Parameter.LAYER_DEPTH; ++k) { //наложение сгенерированных карт друг на друга 56 | Map map = new Map(originalHeight, originalWidth); 57 | for (int initials = 0; initials < height * width * Parameter.INITIAL_PERCENTAGE / 100.0; initials++) { 58 | int y = (int) (Math.random() * (height-2)+1); 59 | int x = (int) (Math.random() * (width-2)+1); 60 | map.set(y, x, Parameter.WALL); 61 | } 62 | for (int i = 0; i < Parameter.AUTOMATA_DEPTH; ++i) { // глубина просчета клеток автоматом 63 | for (int h = height - 1; h >= 0; h--) { 64 | for (int w = 0; w < width; w++) { 65 | makeLikeNeighbours(map, h, w); 66 | mirrorCell(map, h, w); 67 | } 68 | } 69 | } 70 | resultMap.impose(map); 71 | } 72 | return resultMap; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/match/MatchConfig.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.match; 2 | 3 | import ru.croccode.hypernull.domain.MatchMode; 4 | import ru.croccode.hypernull.util.Check; 5 | 6 | public class MatchConfig { 7 | 8 | private final MatchMode mode; 9 | 10 | private final int numRounds; 11 | 12 | private final long randomSeed; 13 | 14 | private final long moveTimeLimit; 15 | 16 | private final int coinSpawnPeriod; 17 | 18 | private final int coinSpawnVolume; 19 | 20 | private MatchConfig(Builder builder) { 21 | Check.notNull(builder); 22 | 23 | this.randomSeed = builder.randomSeed; 24 | this.numRounds = builder.numRounds; 25 | this.mode = builder.mode; 26 | this.moveTimeLimit = builder.moveTimeLimit; 27 | this.coinSpawnPeriod = builder.coinSpawnPeriod; 28 | this.coinSpawnVolume = builder.coinSpawnVolume; 29 | } 30 | 31 | public long getRandomSeed() { 32 | return randomSeed; 33 | } 34 | 35 | public static Builder newBuilder() { 36 | return new Builder(); 37 | } 38 | 39 | public int getNumRounds() { 40 | return numRounds; 41 | } 42 | 43 | public MatchMode getMode() { 44 | return mode; 45 | } 46 | 47 | public long getMoveTimeLimit() { 48 | return moveTimeLimit; 49 | } 50 | 51 | public int getCoinSpawnPeriod() { 52 | return coinSpawnPeriod; 53 | } 54 | 55 | public int getCoinSpawnVolume() { 56 | return coinSpawnVolume; 57 | } 58 | 59 | public static class Builder { 60 | 61 | private long randomSeed = System.currentTimeMillis(); 62 | 63 | private int numRounds = 500; 64 | 65 | private MatchMode mode = MatchMode.FRIENDLY; 66 | 67 | private long moveTimeLimit = 1000; 68 | 69 | private int coinSpawnPeriod = 3; 70 | 71 | private int coinSpawnVolume = 4; 72 | 73 | private Builder() { 74 | } 75 | 76 | public Builder setRandomSeed(long randomSeed) { 77 | this.randomSeed = randomSeed; 78 | return this; 79 | } 80 | 81 | public Builder setNumRounds(int numRounds) { 82 | Check.condition(numRounds > 0); 83 | this.numRounds = numRounds; 84 | return this; 85 | } 86 | 87 | public Builder setMode(MatchMode mode) { 88 | Check.notNull(mode); 89 | this.mode = mode; 90 | return this; 91 | } 92 | 93 | // 0 - no time limit 94 | public Builder setMoveTimeLimit(long moveTimeLimit) { 95 | Check.condition(moveTimeLimit >= 0L); 96 | this.moveTimeLimit = moveTimeLimit; 97 | return this; 98 | } 99 | 100 | public Builder setCoinSpawnPeriod(int coinSpawnPeriod) { 101 | Check.condition(coinSpawnPeriod > 0); 102 | this.coinSpawnPeriod = coinSpawnPeriod; 103 | return this; 104 | } 105 | 106 | public Builder setCoinSpawnVolume(int coinSpawnVolume) { 107 | Check.condition(coinSpawnVolume > 0); 108 | this.coinSpawnVolume = coinSpawnVolume; 109 | return this; 110 | } 111 | 112 | public MatchConfig build() { 113 | return new MatchConfig(this); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /common/src/main/java/ru/croccode/hypernull/geometry/Point.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.geometry; 2 | 3 | import java.util.Objects; 4 | import java.util.function.Consumer; 5 | 6 | import ru.croccode.hypernull.util.Check; 7 | 8 | public class Point { 9 | 10 | private final int x; 11 | private final int y; 12 | 13 | public Point(int x, int y) { 14 | this.x = x; 15 | this.y = y; 16 | } 17 | 18 | public int x() { 19 | return x; 20 | } 21 | 22 | public int y() { 23 | return y; 24 | } 25 | 26 | private static int absMod(int value, int mod) { 27 | int r = value % mod; 28 | return r < 0 ? mod + r : r; 29 | } 30 | 31 | public Point fit(Size size) { 32 | Check.notNull(size); 33 | if (x >= 0 && x < size.width() 34 | && y >= 0 && y < size.height()) 35 | return this; 36 | return new Point( 37 | absMod(x, size.width()), 38 | absMod(y, size.height())); 39 | } 40 | 41 | public Point apply(Offset offset) { 42 | Check.notNull(offset); 43 | if (offset.dx() == 0 && offset.dy() == 0) 44 | return this; 45 | return new Point(x + offset.dx(), y + offset.dy()); 46 | } 47 | 48 | public Point apply(Offset offset, Size size) { 49 | return apply(offset).fit(size); 50 | } 51 | 52 | public Offset offsetTo(Point point, Size size) { 53 | Check.notNull(point); 54 | Check.notNull(size); 55 | 56 | int w = size.width(); 57 | int h = size.height(); 58 | 59 | Point p1 = fit(size); 60 | Point p2 = point.fit(size); 61 | int dx = p2.x - p1.x; 62 | if (Math.abs(dx) > w - Math.abs(dx)) 63 | dx += p2.x > p1.x ? -w : w; 64 | int dy = p2.y - p1.y; 65 | if (Math.abs(dy) > h - Math.abs(dy)) 66 | dy += p2.y > p1.y ? -h : h; 67 | return new Offset(dx, dy); 68 | } 69 | 70 | public Point reflectX(Size size) { 71 | Check.notNull(size); 72 | return new Point(size.width() - x - 1, y).fit(size); 73 | } 74 | 75 | public Point reflectY(Size size) { 76 | Check.notNull(size); 77 | return new Point(x, size.height() - y - 1).fit(size); 78 | } 79 | 80 | public Point reflectXY(Size size) { 81 | Check.notNull(size); 82 | return new Point(size.width() - x - 1, size.height() - y - 1).fit(size); 83 | } 84 | 85 | public void forRange(int r, Size size, Consumer consumer) { 86 | int r2 = r * r; 87 | for (int dx = -r; dx <= r; dx++) { 88 | for (int dy = -r; dy <= r; dy++) { 89 | int d2 = dx * dx + dy * dy; 90 | if (d2 <= r2) { 91 | consumer.accept(new Point(x + dx, y + dy)); 92 | } 93 | } 94 | } 95 | } 96 | 97 | @Override 98 | public boolean equals(Object obj) { 99 | if (this == obj) 100 | return true; 101 | if (obj == null || getClass() != obj.getClass()) 102 | return false; 103 | Point point = (Point)obj; 104 | return x == point.x && y == point.y; 105 | } 106 | 107 | @Override 108 | public int hashCode() { 109 | return Objects.hash(x, y); 110 | } 111 | 112 | @Override 113 | public String toString() { 114 | return "(" + x + ", " + y + ")"; 115 | } 116 | 117 | public String toLog() { 118 | return x + " " + y; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /player/src/main/java/ru/croccode/hypernull/player/view/InfoView.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.player.view; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | import java.util.function.ToIntFunction; 7 | 8 | import javafx.application.Platform; 9 | import javafx.geometry.Pos; 10 | import javafx.scene.control.Label; 11 | import javafx.scene.layout.HBox; 12 | import javafx.scene.paint.Color; 13 | import ru.croccode.hypernull.domain.MatchMode; 14 | import ru.croccode.hypernull.player.Styles; 15 | import ru.croccode.hypernull.player.model.MatchLog; 16 | import ru.croccode.hypernull.player.model.MatchModel; 17 | import ru.croccode.hypernull.player.model.PlaybackModel; 18 | import ru.croccode.hypernull.util.Check; 19 | 20 | public class InfoView extends HBox { 21 | 22 | private final MatchModel matchModel; 23 | 24 | private final PlaybackModel playbackModel; 25 | 26 | public InfoView(MatchModel matchModel, PlaybackModel playbackModel) { 27 | Check.notNull(matchModel); 28 | Check.notNull(playbackModel); 29 | this.matchModel = matchModel; 30 | this.playbackModel = playbackModel; 31 | 32 | matchModel.getMatch().addListener(((observable, oldValue, newValue) 33 | -> Platform.runLater(this::update))); 34 | playbackModel.getRound().addListener(((observable, oldValue, newValue) 35 | -> Platform.runLater(this::update))); 36 | update(); 37 | 38 | setSpacing(Styles.PADDING_HORIZONTAL); 39 | setFillHeight(true); 40 | setAlignment(Pos.CENTER); 41 | } 42 | 43 | private void update() { 44 | MatchLog match = matchModel.getMatch().get(); 45 | if (match == null) { 46 | getChildren().clear(); 47 | Label dropFiles = new Label("Drag and drop match log file(s) to start replay"); 48 | dropFiles.setTextFill(Styles.TEXT_COLOR); 49 | dropFiles.setFont(Styles.DEFAULT_FONT); 50 | getChildren().add(dropFiles); 51 | } else { 52 | List bots = new ArrayList<>(); 53 | int roundNumber = playbackModel.getRound().get(); 54 | MatchLog.Round round = match.getRounds().get(roundNumber); 55 | if (round != null) { 56 | match.getBotNames().forEach((k, v) -> { 57 | Bot bot = new Bot(); 58 | bot.id = k; 59 | bot.name = v; 60 | bot.color = Styles.botColor(k); 61 | bot.numCoins = round.getBotCoins().getOrDefault(k, 0); 62 | bots.add(bot); 63 | }); 64 | bots.sort(Comparator 65 | .comparingInt((ToIntFunction)(x -> x.numCoins)) 66 | .reversed()); 67 | 68 | getChildren().clear(); 69 | if (match.getMode() == MatchMode.DEATHMATCH) { 70 | Label deathmatch = new Label("\u2620"); 71 | deathmatch.setTextFill(Styles.TEXT_COLOR); 72 | deathmatch.setFont(Styles.INFO_FONT); 73 | getChildren().add(deathmatch); 74 | } 75 | for (Bot bot : bots) { 76 | String score = round.getDeadBots().contains(bot.id) 77 | ? "x_x" 78 | : Integer.toString(bot.numCoins); 79 | Label label = new Label(bot.name + " : " + score); 80 | label.setTextFill(bot.color.desaturate()); 81 | label.setFont(Styles.INFO_FONT); 82 | getChildren().add(label); 83 | } 84 | } 85 | } 86 | } 87 | 88 | static class Bot { 89 | 90 | Integer id; 91 | 92 | String name; 93 | 94 | int numCoins; 95 | 96 | Color color; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /TASKS.md: -------------------------------------------------------------------------------- 1 | # Задачи 2 | 3 | Всем задачам мы назначили сложность: 4 | - `A` сложно 5 | - `B` очень сложно 6 | - `C` крайне сложно 7 | 8 | и расписали состав функций, над которыми ожидаем, что вы поработаете. Каждую из функций можем отдельно прорабатывать и уточнять вместе с вами в процессе. 9 | 10 | Мы ожидаем, что вы примените в процессе работы над проектом знания, которые получили в рамках курса и что код будет структурированным: отдельные модули и алгоритмы вынесены в классы/пакеты. 11 | 12 | ## Бот `A` 13 | 14 | Задача бота - шагать по карте. Есть пример реализации starter-bot, с которого начинаете разработку. Вам предстоит реализовать и комбинировать следующие функции для принятия решения о ходе. 15 | 16 | - Исследование карты. Бот должен стремиться собрать информацию о карте и посетить участки, которые он еще “не видел”. 17 | 18 | - Обход препятствий. Алгоритм обхода может быть как простым, например, поворачивать в случайном направлении при достижении заблокированной ячейки, так и более сложным (A* или поиск в ширину). 19 | 20 | - Сбор монет. При принятии решения, к какой монете должен идти бот, могут учитываться разные факторы, например, расстояние до нее, наличие рядом с ней других ботов и других монет. 21 | 22 | - (опционально) DEATMATCH: убегать от опасных ботов и охотиться на слабых. 23 | 24 | ## Генератор карт `B` 25 | 26 | Генератор на вход принимает параметры карты и формирует на выходе один или несколько файлов карт в описанном формате. Параметры: ширина, высота, количество ботов-участников, количество препятствий на карте: низкое/среднее/высокое. 27 | 28 | - Алгоритм генерации. Необходимо выбрать и реализовать один или несколько алгоритмов задания препятствий. лабиринт, симуляция реального ландшафта, свой вариант. 29 | 30 | - Подбор подходящих параметров view/mining/attack радиусов. Для указанного размера и алгоритма генерации подобрать способ вычисления перечисленных параметров для каждой карты. 31 | 32 | - Автоматический выбор стартовых позиций ботов на карте. 33 | 34 | - (опционально) Симметричность карты. Обеспечить симметричность карт для равенства начальных условий для игроков. 35 | 36 | ## Консольный редактор карт `A` 37 | 38 | Редактор на вход принимает путь к файлу карты, выводит ее на экран, позволяет изменять через команды пользователя и сохранять результат. 39 | 40 | - Загрузка и отображение карты. 41 | 42 | - Редактирование: изменение размеров, заблокированных ячеек, начальных позиций ботов. 43 | 44 | - Редактирование параметров карты: view/mining/attack радиусов. 45 | 46 | - (опционально) Вместо консольного реализовать графический редактор на JavaFX (с этим пунктом сложность существенно выше, плюс придется самостоятельно разобраться с JavaFX) 47 | 48 | ## Графический визуализатор матчей `C` 49 | 50 | Визуализатор (или плеер) на вход получает путь к файлу лога матча и проигрывает анимацию матча по раундам. 51 | 52 | - Отображение карты, объектов на ней и событий матча (появление/сбор монет, атаки, перемещения ботов). 53 | 54 | - Вывод названий и текущих весов ботов (количества собранных монет). 55 | 56 | - (опционально) Перемотка (слайдер) и возможность поставить анимацию на паузу. 57 | 58 | ## Статистика и рейтинг `C` 59 | 60 | Приложение анализирует накопленные логи матчей и формирует отчет с различными статистическими параметрами и рейтингом ботов. 61 | 62 | - Список матчей с названиями ботов участников и результатами. 63 | 64 | - Таблица общего рейтинга ботов. 65 | 66 | - Статистика с метриками по каждому боту: например, средний процент исследования карты, процент побед в атаках, скорость/эффективность сбора монет и т.п. 67 | 68 | - (опционально) Отдельные рейтинг ботов по каждой карте. 69 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/map/MapBuilder.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.map; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | import ru.croccode.hypernull.domain.MatchMap; 10 | import ru.croccode.hypernull.geometry.Point; 11 | import ru.croccode.hypernull.geometry.Size; 12 | import ru.croccode.hypernull.util.Check; 13 | 14 | public class MapBuilder { 15 | 16 | public static final int DEFAULT_VIEW_RADIUS = 8; 17 | 18 | public static final int DEFAULT_ATTACK_RADIUS = 4; 19 | 20 | public static final int DEFAULT_MINING_RADIUS = 2; 21 | 22 | private Size size; 23 | 24 | private int viewRadius = DEFAULT_VIEW_RADIUS; 25 | 26 | private int attackRadius = DEFAULT_ATTACK_RADIUS; 27 | 28 | private int miningRadius = DEFAULT_MINING_RADIUS; 29 | 30 | private final Set blocked = new HashSet<>(); 31 | 32 | private final List spawnPositions = new ArrayList<>(); 33 | 34 | public MapBuilder setSize(Size size) { 35 | Check.notNull(size); 36 | this.size = size; 37 | return this; 38 | } 39 | 40 | public MapBuilder setSize(int width, int height) { 41 | this.size = new Size(width, height); 42 | return this; 43 | } 44 | 45 | public MapBuilder setViewRadius(int viewRadius) { 46 | Check.condition(viewRadius > 0); 47 | this.viewRadius = viewRadius; 48 | return this; 49 | } 50 | 51 | public MapBuilder setAttackRadius(int attackRadius) { 52 | Check.condition(viewRadius >= 0); 53 | this.attackRadius = attackRadius; 54 | return this; 55 | } 56 | 57 | public MapBuilder setMiningRadius(int miningRadius) { 58 | Check.condition(viewRadius >= 0); 59 | this.miningRadius = miningRadius; 60 | return this; 61 | } 62 | 63 | public MapBuilder addBlocked(Point point) { 64 | Check.notNull(point); 65 | this.blocked.add(point); 66 | return this; 67 | } 68 | 69 | public MapBuilder addBlocked(int x, int y) { 70 | this.blocked.add(new Point(x, y)); 71 | return this; 72 | } 73 | 74 | public MapBuilder addSpawnPosition(Point point) { 75 | Check.notNull(point); 76 | this.spawnPositions.add(point); 77 | return this; 78 | } 79 | 80 | public MapBuilder addSpawnPosition(int x, int y) { 81 | this.spawnPositions.add(new Point(x, y)); 82 | return this; 83 | } 84 | 85 | public MatchMap build() { 86 | return new SimpleMap(this); 87 | } 88 | 89 | static class SimpleMap implements MatchMap { 90 | 91 | private final Size size; 92 | 93 | private final int viewRadius; 94 | 95 | private final int attackRadius; 96 | 97 | private final int miningRadius; 98 | 99 | private final Set blocked; 100 | 101 | private final List spawnPositions; 102 | 103 | SimpleMap(MapBuilder builder) { 104 | Check.notNull(builder); 105 | Check.notNull(builder.size); 106 | 107 | size = builder.size; 108 | viewRadius = builder.viewRadius; 109 | attackRadius = Math.min(viewRadius - 1, builder.attackRadius); 110 | miningRadius = Math.min(viewRadius - 1, builder.miningRadius); 111 | blocked = new HashSet<>(); 112 | builder.blocked.forEach(p -> blocked.add(p.fit(size))); 113 | spawnPositions = new ArrayList<>(); 114 | builder.spawnPositions.forEach(p -> spawnPositions.add(p.fit(size))); 115 | } 116 | 117 | @Override 118 | public Size getSize() { 119 | return size; 120 | } 121 | 122 | @Override 123 | public int getViewRadius() { 124 | return viewRadius; 125 | } 126 | 127 | @Override 128 | public int getAttackRadius() { 129 | return attackRadius; 130 | } 131 | 132 | @Override 133 | public int getMiningRadius() { 134 | return miningRadius; 135 | } 136 | 137 | @Override 138 | public boolean isBlocked(Point point) { 139 | return blocked.contains(point); 140 | } 141 | 142 | @Override 143 | public List getSpawnPositions() { 144 | return Collections.unmodifiableList(spawnPositions); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/map/RandomMap.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.map; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashSet; 5 | import java.util.List; 6 | import java.util.Random; 7 | import java.util.Set; 8 | 9 | import ru.croccode.hypernull.domain.MatchMap; 10 | import ru.croccode.hypernull.geometry.Offset; 11 | import ru.croccode.hypernull.geometry.Point; 12 | import ru.croccode.hypernull.geometry.Size; 13 | 14 | public class RandomMap implements MatchMap { 15 | 16 | private static final int MIN_WIDTH = 48; 17 | private static final int MAX_WIDTH = 80; 18 | private static final int MIN_HEIGHT = 20; 19 | private static final int MAX_HEIGHT = 40; 20 | 21 | private static final int MIN_BLOCKS = 12; 22 | private static final int MAX_BLOCKS = 32; 23 | private static final int MIN_BLOCK_WIDTH = 2; 24 | private static final int MAX_BLOCK_WIDTH = 8; 25 | private static final int MIN_BLOCK_HEIGHT = 2; 26 | private static final int MAX_BLOCK_HEIGHT = 8; 27 | 28 | private static final int MIN_VIEW_RADIUS = 8; 29 | private static final int MAX_VIEW_RADIUS = 15; 30 | private static final int MIN_ATTACK_RADIUS = 3; 31 | private static final int MAX_ATTACK_RADIUS = 5; 32 | private static final int MIN_MINING_RADIUS = 1; 33 | private static final int MAX_MINING_RADIUS = 3; 34 | 35 | private static final Random rnd = new Random(System.currentTimeMillis()); 36 | 37 | private final Set blocked = new HashSet<>(); 38 | 39 | private final Size size; 40 | 41 | private final int viewRadius; 42 | 43 | private final int miningRadius; 44 | 45 | private final int attackRadius; 46 | 47 | private final List spawnPositions; 48 | 49 | public RandomMap(int numSpawnPositions) { 50 | size = new Size( 51 | MIN_WIDTH + rnd.nextInt(MAX_WIDTH - MIN_WIDTH), 52 | MIN_HEIGHT + rnd.nextInt(MAX_HEIGHT - MIN_HEIGHT) 53 | ); 54 | viewRadius = MIN_VIEW_RADIUS + rnd.nextInt(MAX_VIEW_RADIUS - MIN_VIEW_RADIUS); 55 | attackRadius = Math.min(viewRadius - 1, 56 | MIN_ATTACK_RADIUS + rnd.nextInt(MAX_ATTACK_RADIUS - MIN_ATTACK_RADIUS)); 57 | miningRadius = Math.min(attackRadius - 1, 58 | MIN_MINING_RADIUS + rnd.nextInt(MAX_MINING_RADIUS - MIN_MINING_RADIUS)); 59 | int n = MIN_BLOCKS + rnd.nextInt(MAX_BLOCKS - MIN_BLOCKS); 60 | for (int i = 0; i < n; i++) { 61 | Point rectPoint = new Point( 62 | rnd.nextInt(size.width()), 63 | rnd.nextInt(size.height()) 64 | ); 65 | Size rectSize = new Size( 66 | MIN_BLOCK_WIDTH + rnd.nextInt(MAX_BLOCK_WIDTH - MIN_BLOCK_WIDTH), 67 | MIN_BLOCK_HEIGHT + rnd.nextInt(MAX_BLOCK_HEIGHT - MIN_BLOCK_HEIGHT) 68 | ); 69 | blockRect(rectPoint, rectSize); 70 | } 71 | spawnPositions = new ArrayList<>(); 72 | generate: 73 | while (spawnPositions.size() < numSpawnPositions) { 74 | Point point = new Point( 75 | rnd.nextInt(size.width()), 76 | rnd.nextInt(size.height()) 77 | ); 78 | if (!blocked.contains(point)) { 79 | int r2 = attackRadius * attackRadius; 80 | for (Point generated : spawnPositions) { 81 | int d2 = point.offsetTo(generated, size).length2(); 82 | if (d2 <= r2) 83 | continue generate; 84 | } 85 | spawnPositions.add(point); 86 | } 87 | } 88 | } 89 | 90 | private void blockRect(Point point, Size size) { 91 | for (int dx = 0; dx < size.width(); dx++) { 92 | for (int dy = 0; dy < size.height(); dy++) { 93 | blocked.add(point.apply(new Offset(dx, dy), this.size)); 94 | } 95 | } 96 | } 97 | 98 | @Override 99 | public Size getSize() { 100 | return size; 101 | } 102 | 103 | @Override 104 | public boolean isBlocked(Point point) { 105 | return blocked.contains(point); 106 | } 107 | 108 | @Override 109 | public int getViewRadius() { 110 | return viewRadius; 111 | } 112 | 113 | @Override 114 | public int getMiningRadius() { 115 | return miningRadius; 116 | } 117 | 118 | @Override 119 | public int getAttackRadius() { 120 | return attackRadius; 121 | } 122 | 123 | @Override 124 | public List getSpawnPositions() { 125 | return spawnPositions; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /player/src/main/java/ru/croccode/hypernull/player/MatchLogParser.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.player; 2 | 3 | import java.io.InputStream; 4 | import java.util.Scanner; 5 | 6 | import ru.croccode.hypernull.domain.MatchMode; 7 | import ru.croccode.hypernull.geometry.Point; 8 | import ru.croccode.hypernull.geometry.Size; 9 | import ru.croccode.hypernull.player.model.MatchLog; 10 | import ru.croccode.hypernull.util.Check; 11 | import ru.croccode.hypernull.util.Strings; 12 | 13 | public final class MatchLogParser { 14 | 15 | private MatchLogParser() { 16 | } 17 | 18 | public static MatchLog parse(InputStream in) { 19 | Check.notNull(in); 20 | 21 | MatchLog match = new MatchLog(); 22 | MatchLog.Round round = new MatchLog.Round(); // round zero 23 | round.setRound(0); 24 | match.getRounds().put(0, round); 25 | Scanner s = new Scanner(in); 26 | while (s.hasNext()) { 27 | String line = s.nextLine(); 28 | if (line != null) 29 | line = line.trim(); 30 | if (Strings.isNullOrEmpty(line) || line.startsWith("#")) 31 | continue; 32 | String[] tokens = line.split(" "); 33 | String key = tokens[0]; 34 | 35 | switch (key) { 36 | case "match": 37 | // do nothing 38 | break; 39 | 40 | case "match_id": 41 | match.setMatchId(tokens[1]); 42 | break; 43 | 44 | case "num_bots": 45 | match.setNumBots(Integer.parseInt(tokens[1])); 46 | break; 47 | 48 | case "mode": 49 | match.setMode(MatchMode.valueOf(tokens[1])); 50 | break; 51 | 52 | case "num_rounds": 53 | match.setNumRounds(Integer.parseInt(tokens[1])); 54 | break; 55 | 56 | case "random_seed": 57 | case "move_time_limit": 58 | case "coin_spawn_period": 59 | case "coin_spawn_volume": 60 | // ignore 61 | break; 62 | 63 | case "map_size": 64 | match.setMapSize(new Size( 65 | Integer.parseInt(tokens[1]), 66 | Integer.parseInt(tokens[2]))); 67 | break; 68 | 69 | case "view_radius": 70 | match.setViewRadius(Integer.parseInt(tokens[1])); 71 | break; 72 | 73 | case "mining_radius": 74 | match.setMiningRadius(Integer.parseInt(tokens[1])); 75 | break; 76 | 77 | case "attack_radius": 78 | match.setAttackRadius(Integer.parseInt(tokens[1])); 79 | break; 80 | 81 | case "block": 82 | match.getBlocks().add(new Point( 83 | Integer.parseInt(tokens[1]), 84 | Integer.parseInt(tokens[2]))); 85 | break; 86 | 87 | case "bot_name": 88 | match.getBotNames().put( 89 | Integer.parseInt(tokens[1]), 90 | tokens[2]); 91 | break; 92 | 93 | case "bot": 94 | Point bot = new Point( 95 | Integer.parseInt(tokens[2]), 96 | Integer.parseInt(tokens[3])); 97 | round.getBots().put(Integer.parseInt(tokens[1]), bot); 98 | break; 99 | 100 | case "bot_coins": 101 | round.getBotCoins().put( 102 | Integer.parseInt(tokens[1]), 103 | Integer.parseInt(tokens[2])); 104 | break; 105 | 106 | case "coin": 107 | Point coin = new Point( 108 | Integer.parseInt(tokens[1]), 109 | Integer.parseInt(tokens[2])); 110 | round.getCoins().add(coin); 111 | break; 112 | 113 | case "coin_collected": 114 | Point collected = new Point( 115 | Integer.parseInt(tokens[1]), 116 | Integer.parseInt(tokens[2])); 117 | round.getCoins().remove(collected); 118 | round.getCollectedCoins().add(collected); 119 | break; 120 | 121 | case "round": 122 | MatchLog.Round nextRound = new MatchLog.Round(); 123 | nextRound.getBots().putAll(round.getBots()); 124 | nextRound.getBotCoins().putAll(round.getBotCoins()); 125 | nextRound.getDeadBots().addAll(round.getDeadBots()); 126 | nextRound.getCoins().addAll(round.getCoins()); 127 | round = nextRound; 128 | int num = Integer.parseInt(tokens[1]); 129 | round.setRound(num); 130 | match.getRounds().put(num, round); 131 | break; 132 | 133 | case "attack": 134 | round.getDeadBots().add(Integer.parseInt(tokens[2])); 135 | break; 136 | 137 | case "match_over": 138 | // do nothing 139 | break; 140 | } 141 | } 142 | 143 | return match; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /player/src/main/java/ru/croccode/hypernull/player/model/MatchLog.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.player.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.HashSet; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import java.util.SortedMap; 8 | import java.util.TreeMap; 9 | 10 | import ru.croccode.hypernull.domain.MatchMode; 11 | import ru.croccode.hypernull.geometry.Point; 12 | import ru.croccode.hypernull.geometry.Size; 13 | 14 | public class MatchLog { 15 | 16 | private String matchId; 17 | 18 | private int numBots; 19 | 20 | private MatchMode mode; 21 | 22 | private int numRounds; 23 | 24 | private Size mapSize; 25 | 26 | private int viewRadius; 27 | 28 | private int miningRadius; 29 | 30 | private int attackRadius; 31 | 32 | private Set blocks = new HashSet<>(); 33 | 34 | private Map botNames = new HashMap<>(); 35 | 36 | private SortedMap rounds = new TreeMap<>(); 37 | 38 | public String getMatchId() { 39 | return matchId; 40 | } 41 | 42 | public void setMatchId(String matchId) { 43 | this.matchId = matchId; 44 | } 45 | 46 | public int getNumBots() { 47 | return numBots; 48 | } 49 | 50 | public void setNumBots(int numBots) { 51 | this.numBots = numBots; 52 | } 53 | 54 | public MatchMode getMode() { 55 | return mode; 56 | } 57 | 58 | public void setMode(MatchMode mode) { 59 | this.mode = mode; 60 | } 61 | 62 | public int getNumRounds() { 63 | return numRounds; 64 | } 65 | 66 | public void setNumRounds(int numRounds) { 67 | this.numRounds = numRounds; 68 | } 69 | 70 | public Size getMapSize() { 71 | return mapSize; 72 | } 73 | 74 | public void setMapSize(Size mapSize) { 75 | this.mapSize = mapSize; 76 | } 77 | 78 | public int getViewRadius() { 79 | return viewRadius; 80 | } 81 | 82 | public void setViewRadius(int viewRadius) { 83 | this.viewRadius = viewRadius; 84 | } 85 | 86 | public int getMiningRadius() { 87 | return miningRadius; 88 | } 89 | 90 | public void setMiningRadius(int miningRadius) { 91 | this.miningRadius = miningRadius; 92 | } 93 | 94 | public int getAttackRadius() { 95 | return attackRadius; 96 | } 97 | 98 | public void setAttackRadius(int attackRadius) { 99 | this.attackRadius = attackRadius; 100 | } 101 | 102 | public Set getBlocks() { 103 | return blocks; 104 | } 105 | 106 | public void setBlocks(Set blocks) { 107 | this.blocks = blocks; 108 | } 109 | 110 | public Map getBotNames() { 111 | return botNames; 112 | } 113 | 114 | public void setBotNames(Map botNames) { 115 | this.botNames = botNames; 116 | } 117 | 118 | public SortedMap getRounds() { 119 | return rounds; 120 | } 121 | 122 | public void setRounds(SortedMap rounds) { 123 | this.rounds = rounds; 124 | } 125 | 126 | public static class Round { 127 | 128 | private int round; 129 | 130 | private Map bots = new HashMap<>(); 131 | 132 | private Map botCoins = new HashMap<>(); 133 | 134 | private Set deadBots = new HashSet<>(); 135 | 136 | private Set coins = new HashSet<>(); 137 | 138 | private Set collectedCoins = new HashSet<>(); 139 | 140 | public int getRound() { 141 | return round; 142 | } 143 | 144 | public void setRound(int round) { 145 | this.round = round; 146 | } 147 | 148 | public Map getBots() { 149 | return bots; 150 | } 151 | 152 | public void setBots(Map bots) { 153 | this.bots = bots; 154 | } 155 | 156 | public Map getBotCoins() { 157 | return botCoins; 158 | } 159 | 160 | public Set getDeadBots() { 161 | return deadBots; 162 | } 163 | 164 | public void setDeadBots(Set deadBots) { 165 | this.deadBots = deadBots; 166 | } 167 | 168 | public void setBotCoins(Map botCoins) { 169 | this.botCoins = botCoins; 170 | } 171 | 172 | public Set getCoins() { 173 | return coins; 174 | } 175 | 176 | public void setCoins(Set coins) { 177 | this.coins = coins; 178 | } 179 | 180 | public Set getCollectedCoins() { 181 | return collectedCoins; 182 | } 183 | 184 | public void setCollectedCoins(Set collectedCoins) { 185 | this.collectedCoins = collectedCoins; 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /player/src/main/java/ru/croccode/hypernull/player/view/PlaybackView.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.player.view; 2 | 3 | import javafx.application.Platform; 4 | import javafx.geometry.Pos; 5 | import javafx.scene.control.Button; 6 | import javafx.scene.control.CheckBox; 7 | import javafx.scene.control.Label; 8 | import javafx.scene.control.Slider; 9 | import javafx.scene.layout.HBox; 10 | import javafx.scene.layout.Priority; 11 | import ru.croccode.hypernull.player.Styles; 12 | import ru.croccode.hypernull.player.model.PlaybackModel; 13 | import ru.croccode.hypernull.player.model.MatchModel; 14 | import ru.croccode.hypernull.util.Check; 15 | 16 | public class PlaybackView extends HBox { 17 | 18 | private static final String PLAY_TEXT = "\uF034"; // webdings 19 | private static final String PAUSE_TEXT = "\uF03B"; // webdings 20 | 21 | private final MatchModel matchModel; 22 | private final PlaybackModel playbackModel; 23 | 24 | // controls 25 | private final Button playPause; 26 | private final Slider slider; 27 | private final Label status; 28 | private final CheckBox fog; 29 | 30 | public PlaybackView(MatchModel matchModel, PlaybackModel playbackModel) { 31 | Check.notNull(matchModel); 32 | Check.notNull(playbackModel); 33 | this.matchModel = matchModel; 34 | this.playbackModel = playbackModel; 35 | 36 | // play/pause button 37 | playPause = new Button(); 38 | playPause.setBackground(null); 39 | playPause.setMinWidth(50); 40 | playPause.setAlignment(Pos.CENTER); 41 | playPause.setFont(Styles.WEBDINGS_FONT); 42 | playPause.setTextFill(Styles.TEXT_COLOR); 43 | playPause.setOnAction(e -> { 44 | Platform.runLater(this::togglePause); 45 | }); 46 | 47 | // slider 48 | slider = new Slider(); 49 | slider.setId("playback-slider"); 50 | slider.setMin(0); 51 | slider.setValue(0); 52 | slider.setBlockIncrement(1); 53 | slider.setShowTickMarks(false); 54 | slider.valueProperty().addListener((observable, oldValue, newValue) -> { 55 | Platform.runLater(() -> { 56 | int value = (int)Math.round(slider.getValue()); 57 | slider.setValue(value); 58 | playbackModel.getRound().set(value); 59 | }); 60 | }); 61 | 62 | // status 63 | status = new Label(); 64 | status.setMinWidth(80); 65 | status.setAlignment(Pos.CENTER); 66 | status.setFont(Styles.DEFAULT_FONT); 67 | status.setTextFill(Styles.TEXT_COLOR); 68 | 69 | // fog 70 | fog = new CheckBox("Fog"); 71 | fog.setMinWidth(60); 72 | fog.setFont(Styles.DEFAULT_FONT); 73 | fog.setTextFill(Styles.TEXT_COLOR); 74 | fog.selectedProperty().bindBidirectional(playbackModel.getFog()); 75 | 76 | // listeners 77 | playbackModel.getPaused().addListener((observable, oldValue, newValue) 78 | -> Platform.runLater(this::onPausedChanged)); 79 | playbackModel.getNumRounds().addListener((observable, oldValue, newValue) 80 | -> Platform.runLater(this::onNumRoundsChanged)); 81 | playbackModel.getRound().addListener((observable, oldValue, newValue) 82 | -> Platform.runLater(this::onRoundChanged)); 83 | 84 | // initial callbacks 85 | onPausedChanged(); 86 | onNumRoundsChanged(); 87 | onRoundChanged(); 88 | 89 | // hbox 90 | setAlignment(Pos.CENTER); 91 | setFillHeight(true); 92 | getChildren().add(playPause); 93 | getChildren().add(slider); 94 | getChildren().add(status); 95 | getChildren().add(fog); 96 | HBox.setHgrow(slider, Priority.ALWAYS); 97 | } 98 | 99 | private void togglePause() { 100 | boolean paused = playbackModel.getPaused().get(); 101 | if (paused) { 102 | int round = playbackModel.getRound().get(); 103 | int numRounds = playbackModel.getNumRounds().get(); 104 | if (round >= numRounds) 105 | playbackModel.getRound().set(0); 106 | } 107 | playbackModel.getPaused().set(!paused); 108 | } 109 | 110 | private void onPausedChanged() { 111 | boolean paused = playbackModel.getPaused().get(); 112 | playPause.setText(paused ? PLAY_TEXT : PAUSE_TEXT); 113 | } 114 | 115 | private void onNumRoundsChanged() { 116 | int round = playbackModel.getRound().get(); 117 | int numRounds = playbackModel.getNumRounds().get(); 118 | slider.setMax(numRounds); 119 | status.setText(round + " / " + numRounds); 120 | } 121 | 122 | private void onRoundChanged() { 123 | int round = playbackModel.getRound().get(); 124 | int numRounds = playbackModel.getNumRounds().get(); 125 | int value = (int)Math.round(slider.getValue()); 126 | if (value != round) 127 | slider.setValue(round); 128 | status.setText(round + " / " + numRounds); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /maps/random_3_2.map: -------------------------------------------------------------------------------- 1 | map_size 51 25 2 | view_radius 10 3 | mining_radius 2 4 | attack_radius 3 5 | block 45 0 6 | block 46 0 7 | block 0 1 8 | block 1 1 9 | block 2 1 10 | block 3 1 11 | block 4 1 12 | block 5 1 13 | block 6 1 14 | block 7 1 15 | block 45 1 16 | block 46 1 17 | block 47 1 18 | block 48 1 19 | block 49 1 20 | block 50 1 21 | block 0 2 22 | block 1 2 23 | block 2 2 24 | block 3 2 25 | block 4 2 26 | block 5 2 27 | block 6 2 28 | block 7 2 29 | block 45 2 30 | block 46 2 31 | block 47 2 32 | block 48 2 33 | block 49 2 34 | block 50 2 35 | block 0 3 36 | block 1 3 37 | block 2 3 38 | block 3 3 39 | block 4 3 40 | block 5 3 41 | block 6 3 42 | block 7 3 43 | block 47 3 44 | block 48 3 45 | block 49 3 46 | block 50 3 47 | block 0 4 48 | block 1 4 49 | block 2 4 50 | block 3 4 51 | block 4 4 52 | block 5 4 53 | block 6 4 54 | block 7 4 55 | block 49 4 56 | block 50 4 57 | block 0 5 58 | block 1 5 59 | block 2 5 60 | block 3 5 61 | block 4 5 62 | block 5 5 63 | block 6 5 64 | block 7 5 65 | block 49 5 66 | block 50 5 67 | block 0 6 68 | block 32 6 69 | block 33 6 70 | block 34 6 71 | block 35 6 72 | block 36 6 73 | block 37 6 74 | block 38 6 75 | block 39 6 76 | block 40 6 77 | block 41 6 78 | block 42 6 79 | block 49 6 80 | block 50 6 81 | block 0 7 82 | block 32 7 83 | block 33 7 84 | block 34 7 85 | block 35 7 86 | block 36 7 87 | block 37 7 88 | block 38 7 89 | block 39 7 90 | block 40 7 91 | block 41 7 92 | block 42 7 93 | block 49 7 94 | block 50 7 95 | block 0 8 96 | block 49 8 97 | block 50 8 98 | block 0 9 99 | block 37 9 100 | block 38 9 101 | block 49 9 102 | block 50 9 103 | block 14 10 104 | block 15 10 105 | block 28 10 106 | block 29 10 107 | block 37 10 108 | block 38 10 109 | block 10 11 110 | block 11 11 111 | block 12 11 112 | block 13 11 113 | block 14 11 114 | block 15 11 115 | block 19 11 116 | block 20 11 117 | block 21 11 118 | block 22 11 119 | block 23 11 120 | block 24 11 121 | block 25 11 122 | block 28 11 123 | block 29 11 124 | block 10 12 125 | block 11 12 126 | block 12 12 127 | block 13 12 128 | block 14 12 129 | block 15 12 130 | block 19 12 131 | block 20 12 132 | block 21 12 133 | block 22 12 134 | block 23 12 135 | block 24 12 136 | block 25 12 137 | block 28 12 138 | block 29 12 139 | block 10 13 140 | block 11 13 141 | block 12 13 142 | block 13 13 143 | block 14 13 144 | block 15 13 145 | block 19 13 146 | block 20 13 147 | block 21 13 148 | block 22 13 149 | block 23 13 150 | block 24 13 151 | block 25 13 152 | block 26 13 153 | block 27 13 154 | block 28 13 155 | block 29 13 156 | block 10 14 157 | block 11 14 158 | block 12 14 159 | block 13 14 160 | block 14 14 161 | block 15 14 162 | block 19 14 163 | block 20 14 164 | block 21 14 165 | block 22 14 166 | block 23 14 167 | block 24 14 168 | block 25 14 169 | block 26 14 170 | block 27 14 171 | block 19 15 172 | block 20 15 173 | block 21 15 174 | block 22 15 175 | block 23 15 176 | block 24 15 177 | block 25 15 178 | block 26 15 179 | block 27 15 180 | block 46 15 181 | block 47 15 182 | block 48 15 183 | block 19 16 184 | block 20 16 185 | block 21 16 186 | block 22 16 187 | block 23 16 188 | block 24 16 189 | block 25 16 190 | block 26 16 191 | block 27 16 192 | block 46 16 193 | block 47 16 194 | block 48 16 195 | block 19 17 196 | block 20 17 197 | block 21 17 198 | block 22 17 199 | block 23 17 200 | block 24 17 201 | block 25 17 202 | block 30 17 203 | block 31 17 204 | block 40 17 205 | block 41 17 206 | block 42 17 207 | block 43 17 208 | block 44 17 209 | block 45 17 210 | block 46 17 211 | block 47 17 212 | block 48 17 213 | block 30 18 214 | block 31 18 215 | block 40 18 216 | block 41 18 217 | block 42 18 218 | block 43 18 219 | block 44 18 220 | block 45 18 221 | block 46 18 222 | block 47 18 223 | block 48 18 224 | block 30 19 225 | block 31 19 226 | block 40 19 227 | block 41 19 228 | block 42 19 229 | block 43 19 230 | block 44 19 231 | block 45 19 232 | block 46 19 233 | block 47 19 234 | block 48 19 235 | block 34 20 236 | block 35 20 237 | block 36 20 238 | block 37 20 239 | block 38 20 240 | block 40 20 241 | block 41 20 242 | block 42 20 243 | block 43 20 244 | block 44 20 245 | block 45 20 246 | block 46 20 247 | block 47 20 248 | block 48 20 249 | block 34 21 250 | block 35 21 251 | block 36 21 252 | block 37 21 253 | block 38 21 254 | block 40 21 255 | block 41 21 256 | block 42 21 257 | block 43 21 258 | block 44 21 259 | block 45 21 260 | block 34 22 261 | block 35 22 262 | block 36 22 263 | block 37 22 264 | block 38 22 265 | block 45 24 266 | block 46 24 267 | spawn_position 38 14 268 | spawn_position 14 15 269 | spawn_position 39 17 270 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/server/Server.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.server; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | import java.net.ServerSocket; 6 | import java.net.Socket; 7 | import java.util.ArrayDeque; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.Deque; 11 | import java.util.HashMap; 12 | import java.util.Iterator; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Objects; 16 | import java.util.concurrent.locks.Lock; 17 | import java.util.concurrent.locks.ReentrantLock; 18 | 19 | import ru.croccode.hypernull.domain.MatchMode; 20 | import ru.croccode.hypernull.io.SocketSession; 21 | import ru.croccode.hypernull.message.Hello; 22 | import ru.croccode.hypernull.message.Register; 23 | import ru.croccode.hypernull.util.Check; 24 | import ru.croccode.hypernull.util.Strings; 25 | 26 | public class Server implements Closeable { 27 | 28 | private static final int NUM_ACCEPT_THREADS = 4; 29 | private static final int SOCKET_READ_TIMEOUT_MILLIS = 300_000; 30 | 31 | private final ServerSocket serverSocket; 32 | 33 | // match request queues 34 | private final Map> queues = new HashMap<>(); 35 | 36 | private final Lock lock = new ReentrantLock(); 37 | 38 | public Server(int port) throws IOException { 39 | this.serverSocket = new ServerSocket(port); 40 | for (int i = 0; i < NUM_ACCEPT_THREADS; i++) { 41 | ThreadPools.defaultPool().submit(() -> accept(serverSocket)); 42 | } 43 | } 44 | 45 | private void accept(ServerSocket serverSocket) { 46 | while (true) { 47 | try { 48 | Socket socket = serverSocket.accept(); 49 | socket.setTcpNoDelay(true); 50 | socket.setSoTimeout(SOCKET_READ_TIMEOUT_MILLIS); 51 | SocketSession session = new SocketSession(socket); 52 | sessionOpened(session); 53 | } catch (IOException e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | } 58 | 59 | private boolean authenticate(String botName, String botSecret) { 60 | // TODO check bot secret 61 | return true; 62 | } 63 | 64 | private void sessionOpened(SocketSession session) { 65 | ThreadPools.defaultPool().submit(() -> { 66 | try { 67 | // send hello 68 | session.write(new Hello()); 69 | // wait for register 70 | Register register = session.read(Register.class); 71 | if (register == null) { 72 | session.close(); 73 | return; 74 | } 75 | MatchMode mode = register.getMode() != null 76 | ? register.getMode() 77 | : MatchMode.FRIENDLY; 78 | String botName = Strings.emptyToNull(register.getBotName()); 79 | String botSecret = Strings.emptyToNull(register.getBotSecret()); 80 | if (!authenticate(botName, botSecret)) { 81 | session.close(); 82 | return; 83 | } 84 | MatchRequest request = new MatchRequest(botName, session, mode); 85 | offerRequest(request); 86 | } catch (Exception e) { 87 | e.printStackTrace(); 88 | try { 89 | session.close(); 90 | } catch (IOException e2) { 91 | e2.printStackTrace(); 92 | } 93 | } 94 | }); 95 | } 96 | 97 | private void offerRequest(MatchRequest request) { 98 | lock.lock(); 99 | try { 100 | // discard previous match requests of the bot (by name) 101 | queues.values().forEach(q -> { 102 | Iterator it = q.iterator(); 103 | while (it.hasNext()) { 104 | MatchRequest prev = it.next(); 105 | if (!Objects.equals(prev.getBotName(), request.getBotName())) 106 | continue; 107 | // close request session 108 | try { 109 | prev.getSession().close(); 110 | } catch (IOException ignore) { 111 | } 112 | it.remove(); 113 | } 114 | }); 115 | queues 116 | .computeIfAbsent(request.getMode(), k -> new ArrayDeque<>()) 117 | .offer(request); 118 | } finally { 119 | lock.unlock(); 120 | } 121 | } 122 | 123 | public List pollRequests(MatchMode mode, int min, int max) { 124 | Check.notNull(mode); 125 | lock.lock(); 126 | try { 127 | Deque queue = queues.get(mode); 128 | if (queue == null || queue.size() < min) 129 | return Collections.emptyList(); 130 | 131 | List polled = new ArrayList<>(max); 132 | while (!queue.isEmpty() && polled.size() < max) { 133 | MatchRequest request = queue.poll(); 134 | if (!request.getSession().isOpen()) 135 | continue; 136 | polled.add(request); 137 | } 138 | if (polled.size() < min) { 139 | for (int i = polled.size() - 1; i >= 0; i--) 140 | queue.addFirst(polled.get(i)); 141 | polled.clear(); 142 | } 143 | return polled; 144 | } finally { 145 | lock.unlock(); 146 | } 147 | } 148 | 149 | @Override 150 | public void close() throws IOException { 151 | serverSocket.close(); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /maps/random_2_2.map: -------------------------------------------------------------------------------- 1 | map_size 72 29 2 | view_radius 11 3 | mining_radius 1 4 | attack_radius 3 5 | block 8 0 6 | block 9 0 7 | block 10 0 8 | block 11 0 9 | block 16 0 10 | block 17 0 11 | block 18 0 12 | block 19 0 13 | block 20 0 14 | block 21 0 15 | block 22 0 16 | block 60 0 17 | block 61 0 18 | block 62 0 19 | block 63 0 20 | block 64 0 21 | block 8 1 22 | block 9 1 23 | block 10 1 24 | block 11 1 25 | block 16 1 26 | block 17 1 27 | block 18 1 28 | block 19 1 29 | block 20 1 30 | block 21 1 31 | block 22 1 32 | block 60 1 33 | block 61 1 34 | block 62 1 35 | block 63 1 36 | block 64 1 37 | block 8 2 38 | block 9 2 39 | block 10 2 40 | block 11 2 41 | block 28 2 42 | block 29 2 43 | block 60 2 44 | block 61 2 45 | block 62 2 46 | block 63 2 47 | block 64 2 48 | block 28 3 49 | block 29 3 50 | block 39 3 51 | block 40 3 52 | block 41 3 53 | block 42 3 54 | block 43 3 55 | block 44 3 56 | block 60 3 57 | block 61 3 58 | block 62 3 59 | block 63 3 60 | block 64 3 61 | block 28 4 62 | block 29 4 63 | block 39 4 64 | block 40 4 65 | block 41 4 66 | block 42 4 67 | block 43 4 68 | block 44 4 69 | block 28 5 70 | block 29 5 71 | block 28 6 72 | block 29 6 73 | block 47 6 74 | block 48 6 75 | block 49 6 76 | block 50 6 77 | block 51 6 78 | block 52 6 79 | block 53 6 80 | block 28 7 81 | block 29 7 82 | block 47 7 83 | block 48 7 84 | block 49 7 85 | block 50 7 86 | block 51 7 87 | block 52 7 88 | block 53 7 89 | block 55 7 90 | block 56 7 91 | block 0 8 92 | block 1 8 93 | block 2 8 94 | block 3 8 95 | block 16 8 96 | block 17 8 97 | block 28 8 98 | block 29 8 99 | block 47 8 100 | block 48 8 101 | block 49 8 102 | block 50 8 103 | block 51 8 104 | block 52 8 105 | block 53 8 106 | block 55 8 107 | block 56 8 108 | block 69 8 109 | block 70 8 110 | block 71 8 111 | block 0 9 112 | block 1 9 113 | block 2 9 114 | block 3 9 115 | block 11 9 116 | block 12 9 117 | block 13 9 118 | block 14 9 119 | block 15 9 120 | block 16 9 121 | block 17 9 122 | block 18 9 123 | block 47 9 124 | block 48 9 125 | block 49 9 126 | block 50 9 127 | block 51 9 128 | block 52 9 129 | block 53 9 130 | block 55 9 131 | block 56 9 132 | block 69 9 133 | block 70 9 134 | block 71 9 135 | block 11 10 136 | block 12 10 137 | block 13 10 138 | block 14 10 139 | block 15 10 140 | block 16 10 141 | block 17 10 142 | block 18 10 143 | block 47 10 144 | block 48 10 145 | block 49 10 146 | block 50 10 147 | block 51 10 148 | block 52 10 149 | block 53 10 150 | block 55 10 151 | block 56 10 152 | block 11 11 153 | block 12 11 154 | block 13 11 155 | block 14 11 156 | block 15 11 157 | block 16 11 158 | block 17 11 159 | block 18 11 160 | block 47 11 161 | block 48 11 162 | block 49 11 163 | block 50 11 164 | block 51 11 165 | block 52 11 166 | block 53 11 167 | block 55 11 168 | block 56 11 169 | block 11 12 170 | block 12 12 171 | block 13 12 172 | block 14 12 173 | block 15 12 174 | block 16 12 175 | block 17 14 176 | block 18 14 177 | block 19 14 178 | block 20 14 179 | block 27 14 180 | block 28 14 181 | block 17 15 182 | block 18 15 183 | block 19 15 184 | block 20 15 185 | block 27 15 186 | block 28 15 187 | block 17 16 188 | block 18 16 189 | block 19 16 190 | block 20 16 191 | block 27 16 192 | block 28 16 193 | block 12 19 194 | block 13 19 195 | block 14 19 196 | block 15 19 197 | block 16 19 198 | block 17 19 199 | block 18 19 200 | block 31 19 201 | block 32 19 202 | block 33 19 203 | block 34 19 204 | block 35 19 205 | block 12 20 206 | block 13 20 207 | block 14 20 208 | block 15 20 209 | block 16 20 210 | block 17 20 211 | block 18 20 212 | block 31 20 213 | block 32 20 214 | block 33 20 215 | block 34 20 216 | block 35 20 217 | block 39 20 218 | block 40 20 219 | block 41 20 220 | block 42 20 221 | block 43 20 222 | block 44 20 223 | block 45 20 224 | block 12 21 225 | block 13 21 226 | block 14 21 227 | block 15 21 228 | block 16 21 229 | block 17 21 230 | block 18 21 231 | block 31 21 232 | block 32 21 233 | block 33 21 234 | block 34 21 235 | block 35 21 236 | block 39 21 237 | block 40 21 238 | block 41 21 239 | block 42 21 240 | block 43 21 241 | block 44 21 242 | block 45 21 243 | block 12 22 244 | block 13 22 245 | block 14 22 246 | block 15 22 247 | block 16 22 248 | block 17 22 249 | block 18 22 250 | block 31 22 251 | block 32 22 252 | block 33 22 253 | block 34 22 254 | block 35 22 255 | block 8 26 256 | block 9 26 257 | block 10 26 258 | block 11 26 259 | block 56 26 260 | block 57 26 261 | block 58 26 262 | block 59 26 263 | block 60 26 264 | block 61 26 265 | block 62 26 266 | block 63 26 267 | block 64 26 268 | block 8 27 269 | block 9 27 270 | block 10 27 271 | block 11 27 272 | block 56 27 273 | block 57 27 274 | block 58 27 275 | block 59 27 276 | block 60 27 277 | block 61 27 278 | block 62 27 279 | block 63 27 280 | block 64 27 281 | block 8 28 282 | block 9 28 283 | block 10 28 284 | block 11 28 285 | block 60 28 286 | block 61 28 287 | block 62 28 288 | block 63 28 289 | block 64 28 290 | spawn_position 54 6 291 | spawn_position 10 8 292 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/server/MatchFileLogger.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.server; 2 | 3 | import ru.croccode.hypernull.domain.MatchMap; 4 | import ru.croccode.hypernull.geometry.Point; 5 | import ru.croccode.hypernull.geometry.Size; 6 | import ru.croccode.hypernull.match.MatchConfig; 7 | import ru.croccode.hypernull.match.MatchListener; 8 | 9 | import java.io.Closeable; 10 | import java.io.IOException; 11 | import java.io.PrintWriter; 12 | import java.lang.reflect.Field; 13 | import java.nio.file.Files; 14 | import java.nio.file.Path; 15 | import java.nio.file.Paths; 16 | import java.util.Map; 17 | 18 | public class MatchFileLogger implements MatchListener, Closeable { 19 | 20 | private final static String LOG_FILE_TEMPLATE = "%s/match_%s.log"; 21 | 22 | private final PrintWriter logWriter; 23 | 24 | public MatchFileLogger(String matchId, String logsFolder) { 25 | Path path = Paths.get(logsFolder); 26 | try { 27 | Files.createDirectories(path); 28 | final String logFileName = String.format(LOG_FILE_TEMPLATE, logsFolder, matchId); 29 | this.logWriter = new PrintWriter(logFileName); 30 | } catch (IOException ex) { 31 | throw new RuntimeException( 32 | "Ошибка инициализации логера в файл: " + ex.getMessage(), 33 | ex 34 | ); 35 | } 36 | } 37 | 38 | @Override 39 | public void matchStarted(String id, MatchMap map, MatchConfig config, Map botNames) { 40 | write("match"); 41 | write("match_id " + id); 42 | write("match_time " + System.currentTimeMillis()); 43 | write("num_bots " + botNames.size()); 44 | write("##MatchConfig"); 45 | printAllFields(config); 46 | write("##MapConfig"); 47 | final Size mapSize = map.getSize(); 48 | write("map_size " + mapSize.width() + " " + mapSize.height()); 49 | write("view_radius " + map.getViewRadius()); 50 | write("mining_radius " + map.getMiningRadius()); 51 | write("attack_radius " + map.getAttackRadius()); 52 | printAllBlocks(map); 53 | write("##BotsAndCoinsInfo"); 54 | for (Map.Entry botEntry : botNames.entrySet()) { 55 | write("bot_name " + botEntry.getKey() + " " + botEntry.getValue()); 56 | } 57 | } 58 | 59 | private void printAllBlocks(MatchMap map) { 60 | for (int row = 0; row < map.getHeight(); row++) { 61 | for (int column = 0; column < map.getWidth(); column++) { 62 | Point somePoint = new Point(column, row); 63 | if (map.isBlocked(somePoint)) { 64 | write("block " + somePoint.toLog()); 65 | } 66 | } 67 | } 68 | } 69 | 70 | @Override 71 | public void matchRound(int round) { 72 | write("round " + round); 73 | } 74 | 75 | @Override 76 | public void coinSpawned(Point position) { 77 | write("coin " + position.toLog()); 78 | } 79 | 80 | @Override 81 | public void coinCollected(Point position, K botKey) { 82 | write("coin_collected " + position.toLog() + " " + botKey); 83 | } 84 | 85 | @Override 86 | public void botSpawned(K botKey, Point position) { 87 | botMoved(botKey, position); 88 | } 89 | 90 | @Override 91 | public void botMoved(K botKey, Point position) { 92 | write("bot " + botKey + " " + position.toLog()); 93 | } 94 | 95 | @Override 96 | public void attack(K attackingKey, K defendingKey) { 97 | write("attack " + attackingKey + " " + defendingKey); 98 | } 99 | 100 | @Override 101 | public void botCoinsChanged(K botKey, int numCoins) { 102 | write("bot_coins " + botKey + " " + numCoins); //TODO надо ли выводить предыдущее кол-во монет? 103 | } 104 | 105 | @Override 106 | public void matchOver(K botKey) { 107 | write("match_over " + botKey); 108 | } 109 | 110 | private void write(String msg) { 111 | logWriter.println(msg); 112 | logWriter.flush(); 113 | } 114 | @Override 115 | public void close() { 116 | logWriter.close(); 117 | } 118 | 119 | private void printAllFields(Object someObj) { 120 | final Class objClass = someObj.getClass(); 121 | for (Field declaredField : objClass.getDeclaredFields()) { 122 | try { 123 | declaredField.setAccessible(true); 124 | final String filedName = declaredField.getName() 125 | .replaceAll("([A-Z][a-z])", "_$1") 126 | .toLowerCase(); 127 | write(filedName + " " + declaredField.get(someObj)); 128 | } catch (IllegalAccessException e) { 129 | throw new RuntimeException( 130 | "Ошибка получения значения поля для класса " + objClass.getSimpleName() 131 | + " через рефлексию: " + e.getMessage(), 132 | e 133 | ); 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /player/src/main/java/ru/croccode/hypernull/player/App.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.player; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.net.URL; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.List; 12 | import java.util.concurrent.Executors; 13 | import java.util.concurrent.ScheduledExecutorService; 14 | import java.util.function.Predicate; 15 | 16 | import javafx.application.Application; 17 | import javafx.event.EventHandler; 18 | import javafx.geometry.Pos; 19 | import javafx.scene.Scene; 20 | import javafx.scene.input.Dragboard; 21 | import javafx.scene.input.KeyCode; 22 | import javafx.scene.input.KeyEvent; 23 | import javafx.scene.input.TransferMode; 24 | import javafx.scene.layout.Priority; 25 | import javafx.scene.layout.StackPane; 26 | import javafx.scene.layout.VBox; 27 | import javafx.stage.Stage; 28 | import ru.croccode.hypernull.player.controller.PlaybackController; 29 | import ru.croccode.hypernull.player.model.MatchModel; 30 | import ru.croccode.hypernull.player.model.PlaybackModel; 31 | import ru.croccode.hypernull.player.view.InfoView; 32 | import ru.croccode.hypernull.player.view.MatchView; 33 | import ru.croccode.hypernull.player.view.PlaybackView; 34 | 35 | public class App extends Application { 36 | 37 | public static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1); 38 | 39 | private final MatchModel matchModel; 40 | private final PlaybackModel playbackModel; 41 | private final PlaybackController playbackController; 42 | 43 | public static void main(String[] args) { 44 | launch(args); 45 | } 46 | 47 | public App() { 48 | matchModel = new MatchModel(); 49 | playbackModel = new PlaybackModel(); 50 | playbackController = new PlaybackController(matchModel, playbackModel); 51 | } 52 | 53 | @Override 54 | public void start(Stage primaryStage) { 55 | 56 | // info pane 57 | InfoView infoView = new InfoView(matchModel, playbackModel); 58 | StackPane infoPane = new StackPane(infoView); 59 | infoPane.setMinHeight(60); 60 | infoPane.setAlignment(Pos.CENTER_LEFT); 61 | infoPane.setPadding(Styles.defaultPadding()); 62 | 63 | // canvas pane 64 | MatchView matchView = new MatchView(matchModel, playbackModel); 65 | StackPane canvasPane = matchView; 66 | canvasPane.setMinSize(200, 200); 67 | 68 | // playback pane 69 | PlaybackView playbackView = new PlaybackView(matchModel, playbackModel); 70 | StackPane playbackPane = new StackPane(playbackView); 71 | playbackPane.setMinHeight(50); 72 | playbackPane.setBackground(Styles.solidBackground(Styles.CLEAR_COLOR.brighter())); 73 | playbackPane.setPadding(Styles.defaultPadding()); 74 | StackPane.setAlignment(playbackView, Pos.CENTER); 75 | 76 | // layout 77 | VBox vbox = new VBox(infoPane, canvasPane, playbackPane); 78 | vbox.setFillWidth(true); 79 | VBox.setVgrow(canvasPane, Priority.ALWAYS); 80 | vbox.setBackground(Styles.solidBackground(Styles.CLEAR_COLOR)); 81 | vbox.setOnDragOver(e -> { 82 | if (e.getGestureSource() != vbox && e.getDragboard().hasFiles()) { 83 | e.acceptTransferModes(TransferMode.COPY); 84 | } 85 | e.consume(); 86 | }); 87 | vbox.setOnDragDropped(e -> { 88 | Dragboard board = e.getDragboard(); 89 | boolean done = false; 90 | if (board.hasFiles()) { 91 | try { 92 | queueReplays(board.getFiles()); 93 | } catch (IOException ex) { 94 | ex.printStackTrace(); 95 | } 96 | done = true; 97 | } 98 | e.setDropCompleted(done); 99 | e.consume(); 100 | }); 101 | 102 | Scene scene = new Scene(vbox); 103 | scene.setOnKeyPressed(new EventHandler() { 104 | public void handle(KeyEvent ke) { 105 | if (ke.getCode() == KeyCode.ESCAPE) { 106 | primaryStage.close(); 107 | } 108 | } 109 | }); 110 | URL style = getClass().getResource("/style.css"); 111 | if (style != null) { 112 | scene.getStylesheets().add(style.toExternalForm()); 113 | } 114 | primaryStage.setTitle("¤ HyperNull Player ¤"); 115 | primaryStage.setScene(scene); 116 | primaryStage.setMinWidth(800); 117 | primaryStage.setMinHeight(600); 118 | primaryStage.show(); 119 | 120 | List args = getParameters().getRaw(); 121 | if (!args.isEmpty()) { 122 | List file = Collections.singletonList(new File(args.get(0))); 123 | try { 124 | queueReplays(file); 125 | } catch (IOException ex) { 126 | ex.printStackTrace(); 127 | } 128 | } 129 | } 130 | 131 | @Override 132 | public void stop() { 133 | SCHEDULER.shutdownNow(); 134 | } 135 | 136 | private void queueReplays(List files) throws IOException { 137 | Predicate isReplay = f -> Files.isRegularFile(f) 138 | && f.getFileName().toString().toLowerCase().endsWith(".log"); 139 | List paths = new ArrayList<>(); 140 | if (files != null) { 141 | for (File file : files) { 142 | Path path = file.toPath(); 143 | if (!Files.exists(path)) 144 | continue; 145 | if (Files.isDirectory(path)) { 146 | Files.walk(path) 147 | .filter(isReplay) 148 | .forEach(paths::add); 149 | } else { 150 | if (isReplay.test(path)) 151 | paths.add(path); 152 | } 153 | } 154 | } 155 | matchModel.queueReplays(paths); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/server/AsciiMatchPrinter.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.server; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Set; 10 | 11 | import ru.croccode.hypernull.domain.MatchMap; 12 | import ru.croccode.hypernull.geometry.Point; 13 | import ru.croccode.hypernull.match.MatchConfig; 14 | import ru.croccode.hypernull.match.MatchListener; 15 | import ru.croccode.hypernull.util.Strings; 16 | 17 | public class AsciiMatchPrinter implements MatchListener { 18 | 19 | private static final char FREE = ' '; 20 | private static final char BLOCK = 'X'; 21 | private static final char COIN = '¤'; 22 | private static final char VIEW_MASK = '.'; 23 | private static final char MINING_MASK = '+'; 24 | private static final char ATTACK_MASK = '-'; 25 | 26 | private String matchId; 27 | 28 | private MatchMap map; 29 | 30 | private MatchConfig config; 31 | 32 | private Map bots = new HashMap<>(); 33 | 34 | private Set coins = new HashSet<>(); 35 | 36 | private int round; 37 | 38 | static class BotState { 39 | 40 | String name = Strings.empty(); 41 | 42 | Point position; 43 | 44 | int numCoins = 0; 45 | 46 | boolean alive = true; 47 | } 48 | 49 | @Override 50 | public void matchStarted(String id, MatchMap map, MatchConfig config, Map botNames) { 51 | this.matchId = id; 52 | this.map = map; 53 | this.config = config; 54 | this.bots = new HashMap<>(botNames.size()); 55 | botNames.forEach((k, v) -> { 56 | BotState bot = new BotState(); 57 | bot.name = v; 58 | bots.put(k, bot); 59 | }); 60 | } 61 | 62 | @Override 63 | public void matchRound(int round) { 64 | this.round = round; 65 | printState(); 66 | } 67 | 68 | @Override 69 | public void coinSpawned(Point position) { 70 | coins.add(position); 71 | } 72 | 73 | @Override 74 | public void coinCollected(Point position, Integer botKey) { 75 | coins.remove(position); 76 | } 77 | 78 | @Override 79 | public void botSpawned(Integer botKey, Point position) { 80 | bots.computeIfAbsent(botKey, k -> new BotState()) 81 | .position = position; 82 | } 83 | 84 | @Override 85 | public void botMoved(Integer botKey, Point position) { 86 | bots.computeIfAbsent(botKey, k -> new BotState()) 87 | .position = position; 88 | } 89 | 90 | @Override 91 | public void attack(Integer attackingKey, Integer defendingKey) { 92 | } 93 | 94 | @Override 95 | public void botCoinsChanged(Integer botKey, int numCoins) { 96 | bots.computeIfAbsent(botKey, k -> new BotState()) 97 | .numCoins = numCoins; 98 | } 99 | 100 | @Override 101 | public void matchOver(Integer botKey) { 102 | bots.computeIfAbsent(botKey, k -> new BotState()) 103 | .alive = false; 104 | boolean hasAlive = false; 105 | for (BotState bot : bots.values()) { 106 | if (bot.alive) { 107 | hasAlive = true; 108 | break; 109 | } 110 | } 111 | if (!hasAlive) 112 | printState(); 113 | } 114 | 115 | private void printState() { 116 | System.out.print("\033[H\033[2J"); 117 | if (!Strings.isNullOrEmpty(matchId)) 118 | System.out.println("| MATCH " + matchId ); 119 | System.out.println("| ROUND " + round ); 120 | List botKeys = new ArrayList<>(bots.keySet()); 121 | botKeys.sort(Comparator.naturalOrder()); 122 | for (Integer botKey : botKeys) { 123 | BotState bot = bots.get(botKey); 124 | System.out.print("| " + botKey); 125 | if (!Strings.isNullOrEmpty(bot.name)) 126 | System.out.print(" " + bot.name); 127 | if (bot.alive) 128 | System.out.print(": " + bot.numCoins); 129 | if (!bot.alive) 130 | System.out.print(": X_X"); 131 | System.out.print(" "); 132 | } 133 | System.out.println(); 134 | System.out.println(); 135 | 136 | int viewRadius2 = map.getViewRadius() * map.getViewRadius(); 137 | int miningRadius2 = map.getMiningRadius() * map.getMiningRadius(); 138 | int attackRadius2 = map.getAttackRadius() * map.getAttackRadius(); 139 | for (int y = map.getHeight() - 1; y >= 0; y--) { 140 | for (int x = 0; x < map.getWidth(); x++) { 141 | Point p = new Point(x, y); 142 | char c = FREE; 143 | if (map.isBlocked(p)) { 144 | c = BLOCK; 145 | } else { 146 | if (coins.contains(p)) { 147 | c = COIN; 148 | } else { 149 | boolean isBot = false; 150 | boolean inViewRadius = false; 151 | boolean inMiningRadius = false; 152 | boolean inAttackRadius = false; 153 | for (Map.Entry entry : bots.entrySet()) { 154 | BotState bot = entry.getValue(); 155 | if (!bot.alive || bot.position == null) 156 | continue; 157 | if (bot.position.equals(p)) { 158 | isBot = true; 159 | c = entry.getKey().toString().charAt(0); 160 | break; 161 | } 162 | int d2 = bot.position.offsetTo(p, map.getSize()).length2(); 163 | inViewRadius |= d2 <= viewRadius2; 164 | inMiningRadius |= d2 <= miningRadius2; 165 | inAttackRadius |= d2 <= attackRadius2; 166 | } 167 | if (!isBot) { 168 | if (inMiningRadius) 169 | c = MINING_MASK; 170 | else if (inAttackRadius) 171 | c = ATTACK_MASK; 172 | else if (inViewRadius) 173 | c = VIEW_MASK; 174 | } 175 | } 176 | } 177 | System.out.print(c); 178 | } 179 | System.out.println(); 180 | } 181 | System.out.println(); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/server/MatchRunner.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.server; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.concurrent.CompletableFuture; 10 | import java.util.concurrent.ExecutionException; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import ru.croccode.hypernull.domain.MatchMap; 14 | import ru.croccode.hypernull.geometry.Offset; 15 | import ru.croccode.hypernull.io.SocketSession; 16 | import ru.croccode.hypernull.match.Bot; 17 | import ru.croccode.hypernull.match.Match; 18 | import ru.croccode.hypernull.match.MatchConfig; 19 | import ru.croccode.hypernull.message.MatchOver; 20 | import ru.croccode.hypernull.message.MatchStarted; 21 | import ru.croccode.hypernull.message.Move; 22 | import ru.croccode.hypernull.message.Update; 23 | import ru.croccode.hypernull.util.Check; 24 | import ru.croccode.hypernull.util.Silent; 25 | 26 | public class MatchRunner implements Runnable { 27 | 28 | private final Match match; 29 | 30 | private final Map botSessions; 31 | 32 | public MatchRunner(Match match, Map botSessions) { 33 | Check.notNull(match); 34 | Check.notNull(botSessions); 35 | 36 | this.match = match; 37 | this.botSessions = botSessions; 38 | } 39 | 40 | @Override 41 | public void run() { 42 | 43 | for (Integer botKey : botSessions.keySet()) { 44 | MatchStarted matchStarted = buildMatchStarted(botKey); 45 | try { 46 | botSessions.get(botKey).write(matchStarted); 47 | } catch (IOException e) { 48 | match.deactivateBot(botKey); 49 | e.printStackTrace(); 50 | } 51 | } 52 | 53 | match: 54 | while (match.isActive()) { 55 | 56 | // close inactive bot sessions 57 | List inactive = new ArrayList<>(); 58 | botSessions.forEach((k, v) -> { 59 | if (!match.isActive(k)) 60 | inactive.add(k); 61 | }); 62 | inactive.forEach(this::closeSession); 63 | 64 | Map> responses = new HashMap<>(); 65 | for (Integer botKey : botSessions.keySet()) { 66 | Update update = buildUpdate(botKey); 67 | try { 68 | botSessions.get(botKey).write(update); 69 | // wait for a move message 70 | CompletableFuture response = waitForMove(botKey); 71 | responses.put(botKey, response); 72 | } catch (IOException e) { 73 | match.deactivateBot(botKey); 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | Map botMoves = new HashMap<>(); 79 | for (Map.Entry> entry : responses.entrySet()) { 80 | Integer botKey = entry.getKey(); 81 | CompletableFuture response = entry.getValue(); 82 | Move botMove = null; 83 | try { 84 | botMove = response.get(); 85 | } catch (ExecutionException e) { 86 | e.printStackTrace(); 87 | } catch (InterruptedException e) { 88 | Thread.currentThread().interrupt(); 89 | break match; 90 | } 91 | if (botMove != null) 92 | botMoves.put(botKey, botMove.getOffset()); 93 | } 94 | match.completeRound(botMoves); 95 | } 96 | new ArrayList<>(botSessions.keySet()).forEach(this::closeSession); 97 | } 98 | 99 | private void closeSession(Integer botKey) { 100 | SocketSession session = botSessions.remove(botKey); 101 | if (session == null) 102 | return; 103 | ThreadPools.defaultPool().submit(Silent.runnableOf(() -> { 104 | if (session.isOpen()) 105 | session.write(new MatchOver()); 106 | session.close(); 107 | })); 108 | } 109 | 110 | private CompletableFuture waitForMove(Integer botKey) { 111 | SocketSession session = botSessions.get(botKey); 112 | if (session == null) 113 | return CompletableFuture.completedFuture(null); 114 | CompletableFuture future = CompletableFuture.supplyAsync( 115 | Silent.supplierOf(() -> session.read(Move.class)), 116 | ThreadPools.defaultPool()); 117 | future = future.completeOnTimeout(null, 118 | match.getConfig().getMoveTimeLimit(), TimeUnit.MILLISECONDS); 119 | return future; 120 | } 121 | 122 | private MatchStarted buildMatchStarted(Integer botKey) { 123 | MatchMap map = match.getMap(); 124 | MatchConfig config = match.getConfig(); 125 | 126 | MatchStarted matchStarted = new MatchStarted(); 127 | matchStarted.setNumRounds(config.getNumRounds()); 128 | matchStarted.setMode(config.getMode()); 129 | matchStarted.setMapSize(map.getSize()); 130 | matchStarted.setYourId(botKey); 131 | matchStarted.setViewRadius(map.getViewRadius()); 132 | matchStarted.setMiningRadius(map.getMiningRadius()); 133 | matchStarted.setAttackRadius(map.getAttackRadius()); 134 | matchStarted.setMoveTimeLimit(config.getMoveTimeLimit()); 135 | return matchStarted; 136 | } 137 | 138 | private Update buildUpdate(Integer botKey) { 139 | Update update = new Update(); 140 | update.setRound(match.getRound()); 141 | Set> visibleBots = match.getVisibleBots(botKey); 142 | if (!visibleBots.isEmpty()) { 143 | update.setBots(new HashMap<>()); 144 | update.setBotCoins(new HashMap<>()); 145 | for (Bot bot : visibleBots) { 146 | if (!bot.isActive()) 147 | continue; 148 | update.getBots().put(bot.getKey(), bot.getPosition()); 149 | update.getBotCoins().put(bot.getKey(), bot.getNumCoins()); 150 | } 151 | } 152 | update.setBlocks(match.getVisibleBlocks(botKey)); 153 | update.setCoins(match.getVisibleCoins(botKey)); 154 | return update; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /maps/random_4_2.map: -------------------------------------------------------------------------------- 1 | map_size 51 37 2 | view_radius 13 3 | mining_radius 1 4 | attack_radius 4 5 | block 26 2 6 | block 27 2 7 | block 28 2 8 | block 29 2 9 | block 30 2 10 | block 26 3 11 | block 27 3 12 | block 28 3 13 | block 29 3 14 | block 30 3 15 | block 26 4 16 | block 27 4 17 | block 28 4 18 | block 29 4 19 | block 30 4 20 | block 26 5 21 | block 27 5 22 | block 28 5 23 | block 29 5 24 | block 30 5 25 | block 26 6 26 | block 27 6 27 | block 28 6 28 | block 29 6 29 | block 30 6 30 | block 26 7 31 | block 27 7 32 | block 28 7 33 | block 29 7 34 | block 30 7 35 | block 12 9 36 | block 13 9 37 | block 14 9 38 | block 15 9 39 | block 16 9 40 | block 17 9 41 | block 4 10 42 | block 5 10 43 | block 6 10 44 | block 12 10 45 | block 13 10 46 | block 14 10 47 | block 15 10 48 | block 16 10 49 | block 17 10 50 | block 4 11 51 | block 5 11 52 | block 6 11 53 | block 12 11 54 | block 13 11 55 | block 14 11 56 | block 15 11 57 | block 16 11 58 | block 17 11 59 | block 4 12 60 | block 5 12 61 | block 6 12 62 | block 12 12 63 | block 13 12 64 | block 14 12 65 | block 15 12 66 | block 16 12 67 | block 17 12 68 | block 18 12 69 | block 19 12 70 | block 20 12 71 | block 24 12 72 | block 25 12 73 | block 26 12 74 | block 27 12 75 | block 28 12 76 | block 29 12 77 | block 30 12 78 | block 12 13 79 | block 13 13 80 | block 14 13 81 | block 15 13 82 | block 16 13 83 | block 17 13 84 | block 18 13 85 | block 19 13 86 | block 20 13 87 | block 24 13 88 | block 25 13 89 | block 26 13 90 | block 27 13 91 | block 28 13 92 | block 29 13 93 | block 30 13 94 | block 12 14 95 | block 13 14 96 | block 14 14 97 | block 15 14 98 | block 16 14 99 | block 17 14 100 | block 18 14 101 | block 19 14 102 | block 20 14 103 | block 0 15 104 | block 1 15 105 | block 2 15 106 | block 3 15 107 | block 4 15 108 | block 5 15 109 | block 6 15 110 | block 12 15 111 | block 13 15 112 | block 14 15 113 | block 15 15 114 | block 16 15 115 | block 17 15 116 | block 18 15 117 | block 19 15 118 | block 20 15 119 | block 0 16 120 | block 1 16 121 | block 2 16 122 | block 3 16 123 | block 4 16 124 | block 5 16 125 | block 6 16 126 | block 14 16 127 | block 15 16 128 | block 16 16 129 | block 17 16 130 | block 18 16 131 | block 19 16 132 | block 20 16 133 | block 0 17 134 | block 1 17 135 | block 2 17 136 | block 3 17 137 | block 4 17 138 | block 5 17 139 | block 6 17 140 | block 14 17 141 | block 15 17 142 | block 16 17 143 | block 17 17 144 | block 18 17 145 | block 19 17 146 | block 0 18 147 | block 1 18 148 | block 2 18 149 | block 3 18 150 | block 4 18 151 | block 5 18 152 | block 6 18 153 | block 15 18 154 | block 16 18 155 | block 17 18 156 | block 18 18 157 | block 19 18 158 | block 40 18 159 | block 41 18 160 | block 42 18 161 | block 43 18 162 | block 44 18 163 | block 45 18 164 | block 46 18 165 | block 0 19 166 | block 1 19 167 | block 2 19 168 | block 3 19 169 | block 4 19 170 | block 5 19 171 | block 6 19 172 | block 15 19 173 | block 16 19 174 | block 17 19 175 | block 18 19 176 | block 19 19 177 | block 40 19 178 | block 41 19 179 | block 42 19 180 | block 43 19 181 | block 44 19 182 | block 45 19 183 | block 46 19 184 | block 0 20 185 | block 1 20 186 | block 2 20 187 | block 3 20 188 | block 4 20 189 | block 5 20 190 | block 6 20 191 | block 15 20 192 | block 16 20 193 | block 17 20 194 | block 18 20 195 | block 19 20 196 | block 40 20 197 | block 41 20 198 | block 42 20 199 | block 43 20 200 | block 44 20 201 | block 45 20 202 | block 46 20 203 | block 0 21 204 | block 1 21 205 | block 2 21 206 | block 3 21 207 | block 4 21 208 | block 5 21 209 | block 6 21 210 | block 16 21 211 | block 17 21 212 | block 18 21 213 | block 40 21 214 | block 41 21 215 | block 42 21 216 | block 43 21 217 | block 44 21 218 | block 45 21 219 | block 46 21 220 | block 6 22 221 | block 7 22 222 | block 40 22 223 | block 41 22 224 | block 42 22 225 | block 43 22 226 | block 44 22 227 | block 45 22 228 | block 46 22 229 | block 6 23 230 | block 7 23 231 | block 36 23 232 | block 37 23 233 | block 38 23 234 | block 39 23 235 | block 40 23 236 | block 41 23 237 | block 42 23 238 | block 43 23 239 | block 44 23 240 | block 45 23 241 | block 46 23 242 | block 6 24 243 | block 7 24 244 | block 36 24 245 | block 37 24 246 | block 38 24 247 | block 39 24 248 | block 40 24 249 | block 41 24 250 | block 6 25 251 | block 7 25 252 | block 36 25 253 | block 37 25 254 | block 38 25 255 | block 39 25 256 | block 40 25 257 | block 41 25 258 | block 6 26 259 | block 7 26 260 | block 36 26 261 | block 37 26 262 | block 38 26 263 | block 39 26 264 | block 40 26 265 | block 41 26 266 | block 3 27 267 | block 4 27 268 | block 5 27 269 | block 6 27 270 | block 36 27 271 | block 37 27 272 | block 38 27 273 | block 39 27 274 | block 40 27 275 | block 41 27 276 | block 3 28 277 | block 4 28 278 | block 5 28 279 | block 6 28 280 | block 36 28 281 | block 37 28 282 | block 38 28 283 | block 39 28 284 | block 40 28 285 | block 41 28 286 | block 3 29 287 | block 4 29 288 | block 5 29 289 | block 6 29 290 | block 33 29 291 | block 34 29 292 | block 36 29 293 | block 37 29 294 | block 38 29 295 | block 39 29 296 | block 40 29 297 | block 41 29 298 | block 33 30 299 | block 34 30 300 | block 47 30 301 | block 48 30 302 | block 49 30 303 | block 50 30 304 | block 33 31 305 | block 34 31 306 | block 47 31 307 | block 48 31 308 | block 49 31 309 | block 50 31 310 | block 33 32 311 | block 34 32 312 | block 47 32 313 | block 48 32 314 | block 49 32 315 | block 50 32 316 | block 33 33 317 | block 34 33 318 | block 47 33 319 | block 48 33 320 | block 49 33 321 | block 50 33 322 | block 47 34 323 | block 48 34 324 | block 49 34 325 | block 50 34 326 | spawn_position 3 4 327 | spawn_position 22 4 328 | spawn_position 31 13 329 | spawn_position 43 17 330 | -------------------------------------------------------------------------------- /maps/random_2_1.map: -------------------------------------------------------------------------------- 1 | map_size 48 37 2 | view_radius 10 3 | mining_radius 2 4 | attack_radius 4 5 | block 12 0 6 | block 13 0 7 | block 14 0 8 | block 15 0 9 | block 16 0 10 | block 17 0 11 | block 35 0 12 | block 36 0 13 | block 37 0 14 | block 12 1 15 | block 13 1 16 | block 14 1 17 | block 15 1 18 | block 16 1 19 | block 17 1 20 | block 35 1 21 | block 36 1 22 | block 37 1 23 | block 12 2 24 | block 13 2 25 | block 14 2 26 | block 15 2 27 | block 16 2 28 | block 17 2 29 | block 39 3 30 | block 40 3 31 | block 41 3 32 | block 42 3 33 | block 43 3 34 | block 44 3 35 | block 45 3 36 | block 39 4 37 | block 40 4 38 | block 41 4 39 | block 42 4 40 | block 43 4 41 | block 44 4 42 | block 45 4 43 | block 0 5 44 | block 1 5 45 | block 2 5 46 | block 3 5 47 | block 4 5 48 | block 24 5 49 | block 25 5 50 | block 26 5 51 | block 27 5 52 | block 28 5 53 | block 29 5 54 | block 30 5 55 | block 39 5 56 | block 40 5 57 | block 41 5 58 | block 42 5 59 | block 43 5 60 | block 44 5 61 | block 45 5 62 | block 46 5 63 | block 47 5 64 | block 0 6 65 | block 1 6 66 | block 2 6 67 | block 3 6 68 | block 4 6 69 | block 24 6 70 | block 25 6 71 | block 26 6 72 | block 27 6 73 | block 28 6 74 | block 29 6 75 | block 30 6 76 | block 39 6 77 | block 40 6 78 | block 41 6 79 | block 42 6 80 | block 43 6 81 | block 44 6 82 | block 45 6 83 | block 46 6 84 | block 47 6 85 | block 0 7 86 | block 1 7 87 | block 2 7 88 | block 3 7 89 | block 4 7 90 | block 46 7 91 | block 47 7 92 | block 0 8 93 | block 1 8 94 | block 2 8 95 | block 3 8 96 | block 4 8 97 | block 46 8 98 | block 47 8 99 | block 0 9 100 | block 1 9 101 | block 2 9 102 | block 3 9 103 | block 4 9 104 | block 46 9 105 | block 47 9 106 | block 30 10 107 | block 31 10 108 | block 32 10 109 | block 30 11 110 | block 31 11 111 | block 32 11 112 | block 41 11 113 | block 42 11 114 | block 41 12 115 | block 42 12 116 | block 41 13 117 | block 42 13 118 | block 41 14 119 | block 42 14 120 | block 41 15 121 | block 42 15 122 | block 41 16 123 | block 42 16 124 | block 20 17 125 | block 21 17 126 | block 22 17 127 | block 20 18 128 | block 21 18 129 | block 22 18 130 | block 20 19 131 | block 21 19 132 | block 22 19 133 | block 20 20 134 | block 21 20 135 | block 22 20 136 | block 20 21 137 | block 21 21 138 | block 22 21 139 | block 25 22 140 | block 26 22 141 | block 27 22 142 | block 28 22 143 | block 29 22 144 | block 30 22 145 | block 31 22 146 | block 32 22 147 | block 33 22 148 | block 34 22 149 | block 25 23 150 | block 26 23 151 | block 27 23 152 | block 28 23 153 | block 29 23 154 | block 30 23 155 | block 31 23 156 | block 32 23 157 | block 33 23 158 | block 34 23 159 | block 15 24 160 | block 16 24 161 | block 17 24 162 | block 18 24 163 | block 21 24 164 | block 22 24 165 | block 23 24 166 | block 24 24 167 | block 25 24 168 | block 26 24 169 | block 27 24 170 | block 28 24 171 | block 29 24 172 | block 30 24 173 | block 31 24 174 | block 32 24 175 | block 33 24 176 | block 34 24 177 | block 15 25 178 | block 16 25 179 | block 17 25 180 | block 18 25 181 | block 21 25 182 | block 22 25 183 | block 23 25 184 | block 24 25 185 | block 25 25 186 | block 26 25 187 | block 27 25 188 | block 28 25 189 | block 29 25 190 | block 30 25 191 | block 31 25 192 | block 32 25 193 | block 33 25 194 | block 34 25 195 | block 45 25 196 | block 46 25 197 | block 15 26 198 | block 16 26 199 | block 17 26 200 | block 18 26 201 | block 21 26 202 | block 22 26 203 | block 23 26 204 | block 24 26 205 | block 25 26 206 | block 26 26 207 | block 27 26 208 | block 28 26 209 | block 29 26 210 | block 30 26 211 | block 45 26 212 | block 46 26 213 | block 15 27 214 | block 16 27 215 | block 17 27 216 | block 18 27 217 | block 21 27 218 | block 22 27 219 | block 23 27 220 | block 24 27 221 | block 25 27 222 | block 26 27 223 | block 27 27 224 | block 28 27 225 | block 29 27 226 | block 30 27 227 | block 45 27 228 | block 46 27 229 | block 15 28 230 | block 16 28 231 | block 17 28 232 | block 18 28 233 | block 21 28 234 | block 22 28 235 | block 23 28 236 | block 24 28 237 | block 25 28 238 | block 26 28 239 | block 27 28 240 | block 28 28 241 | block 45 28 242 | block 46 28 243 | block 15 29 244 | block 16 29 245 | block 17 29 246 | block 18 29 247 | block 23 29 248 | block 24 29 249 | block 25 29 250 | block 26 29 251 | block 27 29 252 | block 28 29 253 | block 40 29 254 | block 41 29 255 | block 45 29 256 | block 46 29 257 | block 15 30 258 | block 16 30 259 | block 17 30 260 | block 18 30 261 | block 23 30 262 | block 24 30 263 | block 25 30 264 | block 26 30 265 | block 27 30 266 | block 28 30 267 | block 31 30 268 | block 32 30 269 | block 40 30 270 | block 41 30 271 | block 45 30 272 | block 46 30 273 | block 23 31 274 | block 24 31 275 | block 25 31 276 | block 26 31 277 | block 27 31 278 | block 28 31 279 | block 31 31 280 | block 32 31 281 | block 40 31 282 | block 41 31 283 | block 45 31 284 | block 46 31 285 | block 23 32 286 | block 24 32 287 | block 25 32 288 | block 26 32 289 | block 27 32 290 | block 28 32 291 | block 31 32 292 | block 32 32 293 | block 40 32 294 | block 41 32 295 | block 12 33 296 | block 13 33 297 | block 14 33 298 | block 15 33 299 | block 16 33 300 | block 17 33 301 | block 31 33 302 | block 32 33 303 | block 12 34 304 | block 13 34 305 | block 14 34 306 | block 15 34 307 | block 16 34 308 | block 17 34 309 | block 31 34 310 | block 32 34 311 | block 12 35 312 | block 13 35 313 | block 14 35 314 | block 15 35 315 | block 16 35 316 | block 17 35 317 | block 31 35 318 | block 32 35 319 | block 35 35 320 | block 36 35 321 | block 37 35 322 | block 12 36 323 | block 13 36 324 | block 14 36 325 | block 15 36 326 | block 16 36 327 | block 17 36 328 | block 31 36 329 | block 32 36 330 | block 35 36 331 | block 36 36 332 | block 37 36 333 | spawn_position 24 35 334 | spawn_position 4 18 335 | -------------------------------------------------------------------------------- /maps/map_2_4.map: -------------------------------------------------------------------------------- 1 | map_size 24 45 2 | view_radius 9 3 | mining_radius 2 4 | attack_radius 3 5 | block 23 4 6 | block 23 5 7 | block 23 6 8 | block 23 38 9 | block 23 39 10 | block 23 40 11 | block 22 3 12 | block 22 4 13 | block 22 5 14 | block 22 6 15 | block 22 10 16 | block 22 11 17 | block 22 13 18 | block 22 14 19 | block 22 15 20 | block 22 29 21 | block 22 30 22 | block 22 31 23 | block 22 33 24 | block 22 34 25 | block 22 38 26 | block 22 39 27 | block 22 40 28 | block 22 41 29 | block 21 0 30 | block 21 3 31 | block 21 4 32 | block 21 5 33 | block 21 6 34 | block 21 10 35 | block 21 11 36 | block 21 14 37 | block 21 17 38 | block 21 18 39 | block 21 19 40 | block 21 20 41 | block 21 24 42 | block 21 25 43 | block 21 26 44 | block 21 27 45 | block 21 30 46 | block 21 33 47 | block 21 34 48 | block 21 38 49 | block 21 39 50 | block 21 40 51 | block 21 41 52 | block 21 44 53 | block 20 0 54 | block 20 1 55 | block 20 18 56 | block 20 19 57 | block 20 20 58 | block 20 24 59 | block 20 25 60 | block 20 26 61 | block 20 43 62 | block 20 44 63 | block 19 1 64 | block 19 9 65 | block 19 10 66 | block 19 11 67 | block 19 13 68 | block 19 18 69 | block 19 19 70 | block 19 20 71 | block 19 24 72 | block 19 25 73 | block 19 26 74 | block 19 31 75 | block 19 33 76 | block 19 34 77 | block 19 35 78 | block 19 43 79 | block 18 10 80 | block 18 11 81 | block 18 12 82 | block 18 19 83 | block 18 20 84 | block 18 24 85 | block 18 25 86 | block 18 32 87 | block 18 33 88 | block 18 34 89 | block 17 4 90 | block 17 5 91 | block 17 10 92 | block 17 11 93 | block 17 12 94 | block 17 13 95 | block 17 15 96 | block 17 16 97 | block 17 17 98 | block 17 19 99 | block 17 20 100 | block 17 24 101 | block 17 25 102 | block 17 27 103 | block 17 28 104 | block 17 29 105 | block 17 31 106 | block 17 32 107 | block 17 33 108 | block 17 34 109 | block 17 39 110 | block 17 40 111 | block 16 3 112 | block 16 10 113 | block 16 11 114 | block 16 12 115 | block 16 13 116 | block 16 14 117 | block 16 15 118 | block 16 16 119 | block 16 18 120 | block 16 19 121 | block 16 25 122 | block 16 26 123 | block 16 28 124 | block 16 29 125 | block 16 30 126 | block 16 31 127 | block 16 32 128 | block 16 33 129 | block 16 34 130 | block 16 41 131 | block 15 2 132 | block 15 3 133 | block 15 10 134 | block 15 11 135 | block 15 12 136 | block 15 14 137 | block 15 15 138 | block 15 17 139 | block 15 19 140 | block 15 25 141 | block 15 27 142 | block 15 29 143 | block 15 30 144 | block 15 32 145 | block 15 33 146 | block 15 34 147 | block 15 41 148 | block 15 42 149 | block 14 2 150 | block 14 3 151 | block 14 10 152 | block 14 11 153 | block 14 15 154 | block 14 16 155 | block 14 20 156 | block 14 24 157 | block 14 28 158 | block 14 29 159 | block 14 33 160 | block 14 34 161 | block 14 41 162 | block 14 42 163 | block 13 10 164 | block 13 12 165 | block 13 15 166 | block 13 16 167 | block 13 20 168 | block 13 24 169 | block 13 28 170 | block 13 29 171 | block 13 32 172 | block 13 34 173 | block 10 10 174 | block 10 12 175 | block 10 15 176 | block 10 16 177 | block 10 20 178 | block 10 24 179 | block 10 28 180 | block 10 29 181 | block 10 32 182 | block 10 34 183 | block 9 2 184 | block 9 3 185 | block 9 10 186 | block 9 11 187 | block 9 15 188 | block 9 16 189 | block 9 20 190 | block 9 24 191 | block 9 28 192 | block 9 29 193 | block 9 33 194 | block 9 34 195 | block 9 41 196 | block 9 42 197 | block 8 2 198 | block 8 3 199 | block 8 10 200 | block 8 11 201 | block 8 12 202 | block 8 14 203 | block 8 15 204 | block 8 17 205 | block 8 19 206 | block 8 25 207 | block 8 27 208 | block 8 29 209 | block 8 30 210 | block 8 32 211 | block 8 33 212 | block 8 34 213 | block 8 41 214 | block 8 42 215 | block 7 3 216 | block 7 10 217 | block 7 11 218 | block 7 12 219 | block 7 13 220 | block 7 14 221 | block 7 15 222 | block 7 16 223 | block 7 18 224 | block 7 19 225 | block 7 25 226 | block 7 26 227 | block 7 28 228 | block 7 29 229 | block 7 30 230 | block 7 31 231 | block 7 32 232 | block 7 33 233 | block 7 34 234 | block 7 41 235 | block 6 4 236 | block 6 5 237 | block 6 10 238 | block 6 11 239 | block 6 12 240 | block 6 13 241 | block 6 15 242 | block 6 16 243 | block 6 17 244 | block 6 19 245 | block 6 20 246 | block 6 24 247 | block 6 25 248 | block 6 27 249 | block 6 28 250 | block 6 29 251 | block 6 31 252 | block 6 32 253 | block 6 33 254 | block 6 34 255 | block 6 39 256 | block 6 40 257 | block 5 10 258 | block 5 11 259 | block 5 12 260 | block 5 19 261 | block 5 20 262 | block 5 24 263 | block 5 25 264 | block 5 32 265 | block 5 33 266 | block 5 34 267 | block 4 1 268 | block 4 9 269 | block 4 10 270 | block 4 11 271 | block 4 13 272 | block 4 18 273 | block 4 19 274 | block 4 20 275 | block 4 24 276 | block 4 25 277 | block 4 26 278 | block 4 31 279 | block 4 33 280 | block 4 34 281 | block 4 35 282 | block 4 43 283 | block 3 0 284 | block 3 1 285 | block 3 18 286 | block 3 19 287 | block 3 20 288 | block 3 24 289 | block 3 25 290 | block 3 26 291 | block 3 43 292 | block 3 44 293 | block 2 0 294 | block 2 3 295 | block 2 4 296 | block 2 5 297 | block 2 6 298 | block 2 10 299 | block 2 11 300 | block 2 14 301 | block 2 17 302 | block 2 18 303 | block 2 19 304 | block 2 20 305 | block 2 24 306 | block 2 25 307 | block 2 26 308 | block 2 27 309 | block 2 30 310 | block 2 33 311 | block 2 34 312 | block 2 38 313 | block 2 39 314 | block 2 40 315 | block 2 41 316 | block 2 44 317 | block 1 3 318 | block 1 4 319 | block 1 5 320 | block 1 6 321 | block 1 10 322 | block 1 11 323 | block 1 13 324 | block 1 14 325 | block 1 15 326 | block 1 29 327 | block 1 30 328 | block 1 31 329 | block 1 33 330 | block 1 34 331 | block 1 38 332 | block 1 39 333 | block 1 40 334 | block 1 41 335 | block 0 4 336 | block 0 5 337 | block 0 6 338 | block 0 38 339 | block 0 39 340 | block 0 40 341 | spawn_position 7 7 342 | spawn_position 16 37 343 | -------------------------------------------------------------------------------- /player/src/main/java/ru/croccode/hypernull/player/view/MatchView.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.player.view; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import javafx.application.Platform; 7 | import javafx.geometry.Pos; 8 | import javafx.scene.canvas.Canvas; 9 | import javafx.scene.canvas.GraphicsContext; 10 | import javafx.scene.layout.StackPane; 11 | import javafx.scene.paint.Color; 12 | import ru.croccode.hypernull.geometry.Point; 13 | import ru.croccode.hypernull.geometry.Size; 14 | import ru.croccode.hypernull.player.Styles; 15 | import ru.croccode.hypernull.player.model.MatchLog; 16 | import ru.croccode.hypernull.player.model.PlaybackModel; 17 | import ru.croccode.hypernull.player.model.MatchModel; 18 | import ru.croccode.hypernull.util.Check; 19 | 20 | public class MatchView extends StackPane { 21 | 22 | private final MatchModel matchModel; 23 | 24 | private final PlaybackModel playbackModel; 25 | 26 | private final Canvas canvas; 27 | 28 | public MatchView(MatchModel matchModel, PlaybackModel playbackModel) { 29 | Check.notNull(matchModel); 30 | Check.notNull(playbackModel); 31 | this.matchModel = matchModel; 32 | this.playbackModel = playbackModel; 33 | 34 | canvas = new Canvas(); 35 | canvas.setWidth(getWidth() - 2 * Styles.CANVAS_PADDING); 36 | widthProperty().addListener((observable, old, value) -> { 37 | canvas.setWidth(getWidth() - 2 * Styles.CANVAS_PADDING); 38 | Platform.runLater(this::render); 39 | }); 40 | canvas.setHeight(getHeight() - 2 * Styles.CANVAS_PADDING); 41 | heightProperty().addListener((observable, old, value) -> { 42 | canvas.setHeight(getHeight() - 2 * Styles.CANVAS_PADDING); 43 | Platform.runLater(this::render); 44 | }); 45 | 46 | // listeners 47 | matchModel.getMatch().addListener((observable, old, value) 48 | -> Platform.runLater(this::render)); 49 | playbackModel.getRound().addListener((observable, old, value) 50 | -> Platform.runLater(this::render)); 51 | playbackModel.getFog().addListener((observable, old, value) 52 | -> Platform.runLater(this::render)); 53 | 54 | // canvas pane 55 | getChildren().add(canvas); 56 | setAlignment(Pos.CENTER); 57 | render(); 58 | } 59 | 60 | private synchronized void render() { 61 | GraphicsContext gc = canvas.getGraphicsContext2D(); 62 | CanvasSize size = new CanvasSize(canvas.getWidth(), canvas.getHeight()); 63 | 64 | gc.clearRect(0, 0, size.w, size.h); 65 | MatchLog match = matchModel.getMatch().get(); 66 | if (match == null) 67 | return; 68 | int roundNumber = playbackModel.getRound().get(); 69 | MatchLog.Round round = match.getRounds().get(roundNumber); 70 | if (round == null) 71 | return; // no round state 72 | 73 | Size mapSize = match.getMapSize(); 74 | // cell length 75 | double l = Math.min( 76 | size.w / mapSize.width(), 77 | size.h / mapSize.height()); 78 | CanvasPoint origin = new CanvasPoint( 79 | 0.5 * (size.w - l * mapSize.width()), 80 | 0.5 * (size.h - l * mapSize.height()) 81 | ); 82 | 83 | CanvasTransform transform = new CanvasTransform(); 84 | transform.mapSize = mapSize; 85 | transform.size = size; 86 | transform.origin = origin; 87 | transform.cellSize = new CanvasSize(l, l); 88 | 89 | boolean fog = playbackModel.getFog().get(); 90 | 91 | // draw blocks 92 | for (Point block : match.getBlocks()) { 93 | Color color = Styles.BLOCK_COLOR; 94 | if (fog && inFog(match, round, block)) { 95 | color = color.darker().darker(); 96 | } 97 | gc.setFill(color); 98 | CanvasPoint p = transform.toCanvasPoint(block); 99 | gc.fillRect(p.x, p.y, l, l); 100 | } 101 | // draw coins 102 | Map coins = new HashMap<>(); 103 | round.getCoins().forEach(p -> coins.put(p, Styles.COIN_COLOR)); 104 | round.getCollectedCoins().forEach(p -> coins.put(p, Styles.COLLECTED_COIN_COLOR)); 105 | coins.forEach((coin, color) -> { 106 | if (fog && inFog(match, round, coin)) { 107 | color = color.darker().darker(); 108 | } 109 | gc.setFill(color); 110 | CanvasPoint p = transform.toCanvasPoint(coin); 111 | double r = 0.25 * l; 112 | double d = 0.5 * l - r; 113 | gc.fillRoundRect(p.x + d, p.y + d, 2 * r, 2 * r, 114 | 1.5 * r, 1.5 * r); 115 | }); 116 | // draw bots 117 | round.getBots().forEach((k, v) -> { 118 | if (round.getDeadBots().contains(k)) 119 | return; 120 | gc.setFill(Styles.botColor(k)); 121 | CanvasPoint p = transform.toCanvasPoint(v); 122 | gc.fillRect(p.x, p.y, l, l); 123 | }); 124 | } 125 | 126 | private boolean inFog(MatchLog log, MatchLog.Round round, Point point) { 127 | int r2 = log.getViewRadius() * log.getViewRadius(); 128 | for (Map.Entry entry : round.getBots().entrySet()) { 129 | int botId = entry.getKey(); 130 | if (round.getDeadBots().contains(botId)) 131 | continue; 132 | Point bot = entry.getValue(); 133 | int d2 = bot.offsetTo(point, log.getMapSize()).length2(); 134 | if (d2 <= r2) 135 | return false; 136 | } 137 | return true; 138 | } 139 | 140 | static class CanvasTransform { 141 | 142 | Size mapSize; 143 | 144 | CanvasSize size; 145 | 146 | CanvasPoint origin; 147 | 148 | CanvasSize cellSize; 149 | 150 | CanvasPoint toCanvasPoint(Point mapPoint) { 151 | return new CanvasPoint( 152 | origin.x + cellSize.w * mapPoint.x(), 153 | origin.y + cellSize.h * (mapSize.height() - mapPoint.y() - 1) 154 | ); 155 | } 156 | } 157 | 158 | static class CanvasPoint { 159 | 160 | double x; 161 | 162 | double y; 163 | 164 | CanvasPoint(double x, double y) { 165 | this.x = x; 166 | this.y = y; 167 | } 168 | } 169 | 170 | static class CanvasSize { 171 | 172 | double w; 173 | 174 | double h; 175 | 176 | public CanvasSize(double w, double h) { 177 | this.w = w; 178 | this.h = h; 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /maps/map_2_1.map: -------------------------------------------------------------------------------- 1 | map_size 25 40 2 | view_radius 9 3 | mining_radius 2 4 | attack_radius 3 5 | block 24 0 6 | block 24 1 7 | block 24 2 8 | block 24 10 9 | block 24 11 10 | block 24 13 11 | block 24 14 12 | block 24 15 13 | block 24 16 14 | block 24 23 15 | block 24 24 16 | block 24 25 17 | block 24 26 18 | block 24 28 19 | block 24 29 20 | block 24 37 21 | block 24 38 22 | block 24 39 23 | block 23 0 24 | block 23 1 25 | block 23 2 26 | block 23 3 27 | block 23 9 28 | block 23 10 29 | block 23 11 30 | block 23 12 31 | block 23 13 32 | block 23 14 33 | block 23 15 34 | block 23 16 35 | block 23 23 36 | block 23 24 37 | block 23 25 38 | block 23 26 39 | block 23 27 40 | block 23 28 41 | block 23 29 42 | block 23 30 43 | block 23 36 44 | block 23 37 45 | block 23 38 46 | block 23 39 47 | block 22 0 48 | block 22 1 49 | block 22 2 50 | block 22 3 51 | block 22 8 52 | block 22 9 53 | block 22 10 54 | block 22 12 55 | block 22 13 56 | block 22 14 57 | block 22 25 58 | block 22 26 59 | block 22 27 60 | block 22 29 61 | block 22 30 62 | block 22 31 63 | block 22 36 64 | block 22 37 65 | block 22 38 66 | block 22 39 67 | block 21 0 68 | block 21 1 69 | block 21 2 70 | block 21 3 71 | block 21 8 72 | block 21 9 73 | block 21 10 74 | block 21 12 75 | block 21 13 76 | block 21 14 77 | block 21 15 78 | block 21 16 79 | block 21 17 80 | block 21 22 81 | block 21 23 82 | block 21 24 83 | block 21 25 84 | block 21 26 85 | block 21 27 86 | block 21 29 87 | block 21 30 88 | block 21 31 89 | block 21 36 90 | block 21 37 91 | block 21 38 92 | block 21 39 93 | block 20 0 94 | block 20 1 95 | block 20 2 96 | block 20 3 97 | block 20 9 98 | block 20 12 99 | block 20 13 100 | block 20 14 101 | block 20 15 102 | block 20 16 103 | block 20 23 104 | block 20 24 105 | block 20 25 106 | block 20 26 107 | block 20 27 108 | block 20 30 109 | block 20 36 110 | block 20 37 111 | block 20 38 112 | block 20 39 113 | block 19 1 114 | block 19 2 115 | block 19 3 116 | block 19 13 117 | block 19 14 118 | block 19 15 119 | block 19 24 120 | block 19 25 121 | block 19 26 122 | block 19 36 123 | block 19 37 124 | block 19 38 125 | block 18 1 126 | block 18 4 127 | block 18 17 128 | block 18 22 129 | block 18 35 130 | block 18 38 131 | block 17 7 132 | block 17 8 133 | block 17 9 134 | block 17 10 135 | block 17 11 136 | block 17 12 137 | block 17 17 138 | block 17 22 139 | block 17 27 140 | block 17 28 141 | block 17 29 142 | block 17 30 143 | block 17 31 144 | block 17 32 145 | block 16 8 146 | block 16 9 147 | block 16 10 148 | block 16 11 149 | block 16 12 150 | block 16 16 151 | block 16 17 152 | block 16 22 153 | block 16 23 154 | block 16 27 155 | block 16 28 156 | block 16 29 157 | block 16 30 158 | block 16 31 159 | block 15 5 160 | block 15 6 161 | block 15 7 162 | block 15 16 163 | block 15 23 164 | block 15 32 165 | block 15 33 166 | block 15 34 167 | block 14 5 168 | block 14 6 169 | block 14 7 170 | block 14 32 171 | block 14 33 172 | block 14 34 173 | block 13 5 174 | block 13 6 175 | block 13 15 176 | block 13 24 177 | block 13 33 178 | block 13 34 179 | block 11 5 180 | block 11 6 181 | block 11 15 182 | block 11 24 183 | block 11 33 184 | block 11 34 185 | block 10 5 186 | block 10 6 187 | block 10 7 188 | block 10 32 189 | block 10 33 190 | block 10 34 191 | block 9 5 192 | block 9 6 193 | block 9 7 194 | block 9 16 195 | block 9 23 196 | block 9 32 197 | block 9 33 198 | block 9 34 199 | block 8 8 200 | block 8 9 201 | block 8 10 202 | block 8 11 203 | block 8 12 204 | block 8 16 205 | block 8 17 206 | block 8 22 207 | block 8 23 208 | block 8 27 209 | block 8 28 210 | block 8 29 211 | block 8 30 212 | block 8 31 213 | block 7 7 214 | block 7 8 215 | block 7 9 216 | block 7 10 217 | block 7 11 218 | block 7 12 219 | block 7 17 220 | block 7 22 221 | block 7 27 222 | block 7 28 223 | block 7 29 224 | block 7 30 225 | block 7 31 226 | block 7 32 227 | block 6 1 228 | block 6 4 229 | block 6 17 230 | block 6 22 231 | block 6 35 232 | block 6 38 233 | block 5 1 234 | block 5 2 235 | block 5 3 236 | block 5 13 237 | block 5 14 238 | block 5 15 239 | block 5 24 240 | block 5 25 241 | block 5 26 242 | block 5 36 243 | block 5 37 244 | block 5 38 245 | block 4 0 246 | block 4 1 247 | block 4 2 248 | block 4 3 249 | block 4 9 250 | block 4 12 251 | block 4 13 252 | block 4 14 253 | block 4 15 254 | block 4 16 255 | block 4 23 256 | block 4 24 257 | block 4 25 258 | block 4 26 259 | block 4 27 260 | block 4 30 261 | block 4 36 262 | block 4 37 263 | block 4 38 264 | block 4 39 265 | block 3 0 266 | block 3 1 267 | block 3 2 268 | block 3 3 269 | block 3 8 270 | block 3 9 271 | block 3 10 272 | block 3 12 273 | block 3 13 274 | block 3 14 275 | block 3 15 276 | block 3 16 277 | block 3 17 278 | block 3 22 279 | block 3 23 280 | block 3 24 281 | block 3 25 282 | block 3 26 283 | block 3 27 284 | block 3 29 285 | block 3 30 286 | block 3 31 287 | block 3 36 288 | block 3 37 289 | block 3 38 290 | block 3 39 291 | block 2 0 292 | block 2 1 293 | block 2 2 294 | block 2 3 295 | block 2 8 296 | block 2 9 297 | block 2 10 298 | block 2 12 299 | block 2 13 300 | block 2 14 301 | block 2 25 302 | block 2 26 303 | block 2 27 304 | block 2 29 305 | block 2 30 306 | block 2 31 307 | block 2 36 308 | block 2 37 309 | block 2 38 310 | block 2 39 311 | block 1 0 312 | block 1 1 313 | block 1 2 314 | block 1 3 315 | block 1 9 316 | block 1 10 317 | block 1 11 318 | block 1 12 319 | block 1 13 320 | block 1 14 321 | block 1 15 322 | block 1 16 323 | block 1 23 324 | block 1 24 325 | block 1 25 326 | block 1 26 327 | block 1 27 328 | block 1 28 329 | block 1 29 330 | block 1 30 331 | block 1 36 332 | block 1 37 333 | block 1 38 334 | block 1 39 335 | block 0 0 336 | block 0 1 337 | block 0 2 338 | block 0 10 339 | block 0 11 340 | block 0 13 341 | block 0 14 342 | block 0 15 343 | block 0 16 344 | block 0 23 345 | block 0 24 346 | block 0 25 347 | block 0 26 348 | block 0 28 349 | block 0 29 350 | block 0 37 351 | block 0 38 352 | block 0 39 353 | spawn_position 18 5 354 | spawn_position 6 34 355 | -------------------------------------------------------------------------------- /server/src/main/java/ru/croccode/hypernull/server/HyperNull.java: -------------------------------------------------------------------------------- 1 | package ru.croccode.hypernull.server; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Properties; 16 | import java.util.stream.Collectors; 17 | 18 | import ru.croccode.hypernull.domain.MatchMap; 19 | import ru.croccode.hypernull.domain.MatchMode; 20 | import ru.croccode.hypernull.io.SocketSession; 21 | import ru.croccode.hypernull.map.MapRegistry; 22 | import ru.croccode.hypernull.map.MapStore; 23 | import ru.croccode.hypernull.map.RandomMap; 24 | import ru.croccode.hypernull.match.Match; 25 | import ru.croccode.hypernull.match.MatchConfig; 26 | import ru.croccode.hypernull.match.MatchListener; 27 | import ru.croccode.hypernull.util.Check; 28 | import ru.croccode.hypernull.util.Silent; 29 | 30 | public class HyperNull implements Runnable, Closeable { 31 | 32 | private static final int MIN_FRIENDLY_BOTS = 1; 33 | private static final int MAX_FRIENDLY_BOTS = 2; 34 | private static final int MIN_DEATHMATCH_BOTS = 2; 35 | private static final int MAX_DEATHMATCH_BOTS = 2; 36 | 37 | private final MapRegistry mapRegistry; 38 | 39 | private final Server server; 40 | 41 | private final String matchLogsFolder; 42 | 43 | private final boolean matchLogStdout; 44 | 45 | public HyperNull(Properties properties) throws IOException { 46 | Check.notNull(properties); 47 | // start server 48 | int serverPort = Integer.parseInt( 49 | properties.getProperty("server.port", "2021")); 50 | matchLogsFolder = properties.getProperty("match.log.folder","./matchlogs/"); 51 | System.out.println("Match logs folder was set to: " + matchLogsFolder); 52 | matchLogStdout = Boolean.parseBoolean( 53 | properties.getProperty("match.log.stdout", "false")); 54 | System.out.println("Log matches to stdout: " + matchLogStdout); 55 | String mapsFolder = properties.getProperty("maps.folder","./maps/"); 56 | System.out.println("Maps folder was set to: " + mapsFolder); 57 | mapRegistry = MapStore.load(Paths.get(mapsFolder)); 58 | this.server = new Server(serverPort); 59 | System.out.println("Server started on port: " + serverPort); 60 | } 61 | 62 | @Override 63 | public void run() { 64 | List modes = Arrays.asList(MatchMode.FRIENDLY, MatchMode.DEATHMATCH); 65 | while (true) { 66 | Collections.shuffle(modes); 67 | boolean matchStarted = false; 68 | for (MatchMode mode : modes) { 69 | List matchRequests = server.pollRequests( 70 | mode, 71 | mode == MatchMode.FRIENDLY ? MIN_FRIENDLY_BOTS : MIN_DEATHMATCH_BOTS, 72 | mode == MatchMode.FRIENDLY ? MAX_FRIENDLY_BOTS : MAX_DEATHMATCH_BOTS); 73 | if (!matchRequests.isEmpty()) { 74 | runMatch(mode, matchRequests); 75 | matchStarted = true; 76 | } 77 | } 78 | if (!matchStarted) { 79 | try { 80 | Thread.sleep(1_000L); 81 | } catch (InterruptedException e) { 82 | Thread.currentThread().interrupt(); 83 | break; 84 | } 85 | } 86 | } 87 | } 88 | 89 | private void runMatch(MatchMode mode, List matchRequests) { 90 | int numBots = matchRequests.size(); 91 | if (numBots == 0) 92 | return; 93 | 94 | Map botNames = new HashMap<>(); 95 | Map botSessions = new HashMap<>(); 96 | Integer botKey = 0; 97 | for (MatchRequest matchRequest : matchRequests) { 98 | botNames.put(botKey, matchRequest.getBotName()); 99 | botSessions.put(botKey, matchRequest.getSession()); 100 | botKey++; 101 | } 102 | 103 | String matchId = MatchId.nextId(); 104 | MatchMap map = mapRegistry.randomMap(numBots); 105 | if (map == null) { 106 | // generate random map 107 | map = new RandomMap(numBots); 108 | } 109 | MatchConfig config = buildMatchConfig(mode, map); 110 | try (MatchFileLogger fileLogger = new MatchFileLogger<>(matchId, this.matchLogsFolder)) { 111 | List> listeners = new ArrayList<>(); 112 | if (matchLogStdout) { 113 | listeners.add(new AsciiMatchPrinter()); 114 | } 115 | listeners.add(fileLogger); 116 | Match match = new Match<>(matchId, map, config, botNames, listeners); 117 | 118 | String bots = matchRequests.stream() 119 | .map(r -> r.getBotName() + " [" + r.getMode() + "]") 120 | .collect(Collectors.joining(", ")); 121 | System.out.println("Starting match " + matchId + ": " + bots); 122 | new MatchRunner(match, botSessions).run(); 123 | } 124 | } 125 | 126 | private MatchConfig buildMatchConfig(MatchMode mode, MatchMap map) { 127 | return MatchConfig.newBuilder() 128 | .setNumRounds(500) 129 | .setMode(mode) 130 | .setMoveTimeLimit(1_000L) 131 | .setCoinSpawnPeriod(5) 132 | .setCoinSpawnVolume(2) 133 | .build(); 134 | } 135 | 136 | @Override 137 | public void close() throws IOException { 138 | server.close(); 139 | } 140 | 141 | public static void main(String[] args) throws IOException { 142 | System.out.println("¤ ¤ ¤ HyperNull STARTING... ¤ ¤ ¤"); 143 | String configPath = args.length > 0 144 | ? args[0] 145 | : "hypernull.properties"; 146 | Properties properties = new Properties(); 147 | Path path = Paths.get(configPath); 148 | if (Files.exists(path)) { 149 | try (InputStream in = Files.newInputStream(path)) { 150 | properties.load(in); 151 | } 152 | } 153 | 154 | HyperNull app = new HyperNull(properties); 155 | Runtime.getRuntime().addShutdownHook(new Thread(Silent.runnableOf(() -> { 156 | app.close(); 157 | ThreadPools.shutdownAll(); 158 | }))); 159 | System.out.println("¤ ¤ ¤ HyperNull READY ¤ ¤ ¤ "); 160 | app.run(); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /maps/map_2_3.map: -------------------------------------------------------------------------------- 1 | map_size 23 34 2 | view_radius 9 3 | mining_radius 2 4 | attack_radius 3 5 | block 22 5 6 | block 22 6 7 | block 22 7 8 | block 22 11 9 | block 22 12 10 | block 22 13 11 | block 22 14 12 | block 22 19 13 | block 22 20 14 | block 22 21 15 | block 22 22 16 | block 22 26 17 | block 22 27 18 | block 22 28 19 | block 21 4 20 | block 21 5 21 | block 21 6 22 | block 21 7 23 | block 21 8 24 | block 21 9 25 | block 21 10 26 | block 21 11 27 | block 21 12 28 | block 21 13 29 | block 21 14 30 | block 21 19 31 | block 21 20 32 | block 21 21 33 | block 21 22 34 | block 21 23 35 | block 21 24 36 | block 21 25 37 | block 21 26 38 | block 21 27 39 | block 21 28 40 | block 21 29 41 | block 20 4 42 | block 20 5 43 | block 20 6 44 | block 20 7 45 | block 20 8 46 | block 20 9 47 | block 20 10 48 | block 20 11 49 | block 20 12 50 | block 20 21 51 | block 20 22 52 | block 20 23 53 | block 20 24 54 | block 20 25 55 | block 20 26 56 | block 20 27 57 | block 20 28 58 | block 20 29 59 | block 19 2 60 | block 19 3 61 | block 19 4 62 | block 19 5 63 | block 19 6 64 | block 19 7 65 | block 19 8 66 | block 19 9 67 | block 19 10 68 | block 19 23 69 | block 19 24 70 | block 19 25 71 | block 19 26 72 | block 19 27 73 | block 19 28 74 | block 19 29 75 | block 19 30 76 | block 19 31 77 | block 18 2 78 | block 18 3 79 | block 18 4 80 | block 18 5 81 | block 18 7 82 | block 18 8 83 | block 18 9 84 | block 18 24 85 | block 18 25 86 | block 18 26 87 | block 18 28 88 | block 18 29 89 | block 18 30 90 | block 18 31 91 | block 17 1 92 | block 17 2 93 | block 17 3 94 | block 17 7 95 | block 17 8 96 | block 17 9 97 | block 17 14 98 | block 17 15 99 | block 17 18 100 | block 17 19 101 | block 17 24 102 | block 17 25 103 | block 17 26 104 | block 17 30 105 | block 17 31 106 | block 17 32 107 | block 16 1 108 | block 16 2 109 | block 16 3 110 | block 16 4 111 | block 16 5 112 | block 16 6 113 | block 16 7 114 | block 16 8 115 | block 16 9 116 | block 16 14 117 | block 16 15 118 | block 16 18 119 | block 16 19 120 | block 16 24 121 | block 16 25 122 | block 16 26 123 | block 16 27 124 | block 16 28 125 | block 16 29 126 | block 16 30 127 | block 16 31 128 | block 16 32 129 | block 15 1 130 | block 15 2 131 | block 15 3 132 | block 15 4 133 | block 15 5 134 | block 15 6 135 | block 15 7 136 | block 15 8 137 | block 15 12 138 | block 15 13 139 | block 15 14 140 | block 15 19 141 | block 15 20 142 | block 15 21 143 | block 15 25 144 | block 15 26 145 | block 15 27 146 | block 15 28 147 | block 15 29 148 | block 15 30 149 | block 15 31 150 | block 15 32 151 | block 14 1 152 | block 14 2 153 | block 14 3 154 | block 14 4 155 | block 14 7 156 | block 14 12 157 | block 14 13 158 | block 14 14 159 | block 14 19 160 | block 14 20 161 | block 14 21 162 | block 14 26 163 | block 14 29 164 | block 14 30 165 | block 14 31 166 | block 14 32 167 | block 13 1 168 | block 13 2 169 | block 13 11 170 | block 13 12 171 | block 13 13 172 | block 13 14 173 | block 13 19 174 | block 13 20 175 | block 13 21 176 | block 13 22 177 | block 13 31 178 | block 13 32 179 | block 12 1 180 | block 12 2 181 | block 12 4 182 | block 12 6 183 | block 12 10 184 | block 12 11 185 | block 12 12 186 | block 12 13 187 | block 12 20 188 | block 12 21 189 | block 12 22 190 | block 12 23 191 | block 12 27 192 | block 12 29 193 | block 12 31 194 | block 12 32 195 | block 10 1 196 | block 10 2 197 | block 10 4 198 | block 10 6 199 | block 10 10 200 | block 10 11 201 | block 10 12 202 | block 10 13 203 | block 10 20 204 | block 10 21 205 | block 10 22 206 | block 10 23 207 | block 10 27 208 | block 10 29 209 | block 10 31 210 | block 10 32 211 | block 9 1 212 | block 9 2 213 | block 9 11 214 | block 9 12 215 | block 9 13 216 | block 9 14 217 | block 9 19 218 | block 9 20 219 | block 9 21 220 | block 9 22 221 | block 9 31 222 | block 9 32 223 | block 8 1 224 | block 8 2 225 | block 8 3 226 | block 8 4 227 | block 8 7 228 | block 8 12 229 | block 8 13 230 | block 8 14 231 | block 8 19 232 | block 8 20 233 | block 8 21 234 | block 8 26 235 | block 8 29 236 | block 8 30 237 | block 8 31 238 | block 8 32 239 | block 7 1 240 | block 7 2 241 | block 7 3 242 | block 7 4 243 | block 7 5 244 | block 7 6 245 | block 7 7 246 | block 7 8 247 | block 7 12 248 | block 7 13 249 | block 7 14 250 | block 7 19 251 | block 7 20 252 | block 7 21 253 | block 7 25 254 | block 7 26 255 | block 7 27 256 | block 7 28 257 | block 7 29 258 | block 7 30 259 | block 7 31 260 | block 7 32 261 | block 6 1 262 | block 6 2 263 | block 6 3 264 | block 6 4 265 | block 6 5 266 | block 6 6 267 | block 6 7 268 | block 6 8 269 | block 6 9 270 | block 6 14 271 | block 6 15 272 | block 6 18 273 | block 6 19 274 | block 6 24 275 | block 6 25 276 | block 6 26 277 | block 6 27 278 | block 6 28 279 | block 6 29 280 | block 6 30 281 | block 6 31 282 | block 6 32 283 | block 5 1 284 | block 5 2 285 | block 5 3 286 | block 5 7 287 | block 5 8 288 | block 5 9 289 | block 5 14 290 | block 5 15 291 | block 5 18 292 | block 5 19 293 | block 5 24 294 | block 5 25 295 | block 5 26 296 | block 5 30 297 | block 5 31 298 | block 5 32 299 | block 4 2 300 | block 4 3 301 | block 4 4 302 | block 4 5 303 | block 4 7 304 | block 4 8 305 | block 4 9 306 | block 4 24 307 | block 4 25 308 | block 4 26 309 | block 4 28 310 | block 4 29 311 | block 4 30 312 | block 4 31 313 | block 3 2 314 | block 3 3 315 | block 3 4 316 | block 3 5 317 | block 3 6 318 | block 3 7 319 | block 3 8 320 | block 3 9 321 | block 3 10 322 | block 3 23 323 | block 3 24 324 | block 3 25 325 | block 3 26 326 | block 3 27 327 | block 3 28 328 | block 3 29 329 | block 3 30 330 | block 3 31 331 | block 2 4 332 | block 2 5 333 | block 2 6 334 | block 2 7 335 | block 2 8 336 | block 2 9 337 | block 2 10 338 | block 2 11 339 | block 2 12 340 | block 2 21 341 | block 2 22 342 | block 2 23 343 | block 2 24 344 | block 2 25 345 | block 2 26 346 | block 2 27 347 | block 2 28 348 | block 2 29 349 | block 1 4 350 | block 1 5 351 | block 1 6 352 | block 1 7 353 | block 1 8 354 | block 1 9 355 | block 1 10 356 | block 1 11 357 | block 1 12 358 | block 1 13 359 | block 1 14 360 | block 1 19 361 | block 1 20 362 | block 1 21 363 | block 1 22 364 | block 1 23 365 | block 1 24 366 | block 1 25 367 | block 1 26 368 | block 1 27 369 | block 1 28 370 | block 1 29 371 | block 0 5 372 | block 0 6 373 | block 0 7 374 | block 0 11 375 | block 0 12 376 | block 0 13 377 | block 0 14 378 | block 0 19 379 | block 0 20 380 | block 0 21 381 | block 0 22 382 | block 0 26 383 | block 0 27 384 | block 0 28 385 | spawn_position 11 26 386 | spawn_position 11 7 387 | -------------------------------------------------------------------------------- /maps/random_6_2.map: -------------------------------------------------------------------------------- 1 | map_size 59 33 2 | view_radius 11 3 | mining_radius 1 4 | attack_radius 4 5 | block 16 0 6 | block 17 0 7 | block 18 0 8 | block 45 0 9 | block 46 0 10 | block 47 0 11 | block 16 1 12 | block 17 1 13 | block 18 1 14 | block 16 2 15 | block 17 2 16 | block 18 2 17 | block 13 3 18 | block 14 3 19 | block 15 3 20 | block 16 3 21 | block 17 3 22 | block 18 3 23 | block 13 4 24 | block 14 4 25 | block 15 4 26 | block 16 4 27 | block 17 4 28 | block 18 4 29 | block 13 5 30 | block 14 5 31 | block 15 5 32 | block 16 5 33 | block 17 5 34 | block 18 5 35 | block 10 6 36 | block 11 6 37 | block 5 7 38 | block 6 7 39 | block 7 7 40 | block 8 7 41 | block 10 7 42 | block 11 7 43 | block 32 7 44 | block 33 7 45 | block 34 7 46 | block 5 8 47 | block 6 8 48 | block 7 8 49 | block 8 8 50 | block 10 8 51 | block 11 8 52 | block 18 8 53 | block 19 8 54 | block 20 8 55 | block 21 8 56 | block 22 8 57 | block 23 8 58 | block 32 8 59 | block 33 8 60 | block 34 8 61 | block 35 8 62 | block 36 8 63 | block 37 8 64 | block 38 8 65 | block 3 9 66 | block 4 9 67 | block 5 9 68 | block 6 9 69 | block 7 9 70 | block 8 9 71 | block 18 9 72 | block 19 9 73 | block 20 9 74 | block 21 9 75 | block 22 9 76 | block 23 9 77 | block 30 9 78 | block 31 9 79 | block 32 9 80 | block 33 9 81 | block 34 9 82 | block 35 9 83 | block 36 9 84 | block 37 9 85 | block 38 9 86 | block 3 10 87 | block 4 10 88 | block 5 10 89 | block 6 10 90 | block 7 10 91 | block 8 10 92 | block 18 10 93 | block 19 10 94 | block 20 10 95 | block 21 10 96 | block 22 10 97 | block 23 10 98 | block 30 10 99 | block 31 10 100 | block 33 10 101 | block 34 10 102 | block 35 10 103 | block 36 10 104 | block 37 10 105 | block 38 10 106 | block 3 11 107 | block 4 11 108 | block 5 11 109 | block 6 11 110 | block 7 11 111 | block 8 11 112 | block 18 11 113 | block 19 11 114 | block 20 11 115 | block 21 11 116 | block 22 11 117 | block 23 11 118 | block 30 11 119 | block 31 11 120 | block 33 11 121 | block 34 11 122 | block 35 11 123 | block 36 11 124 | block 37 11 125 | block 38 11 126 | block 3 12 127 | block 4 12 128 | block 5 12 129 | block 6 12 130 | block 7 12 131 | block 8 12 132 | block 9 12 133 | block 10 12 134 | block 11 12 135 | block 12 12 136 | block 13 12 137 | block 18 12 138 | block 19 12 139 | block 20 12 140 | block 21 12 141 | block 22 12 142 | block 23 12 143 | block 30 12 144 | block 31 12 145 | block 47 12 146 | block 48 12 147 | block 49 12 148 | block 50 12 149 | block 51 12 150 | block 52 12 151 | block 53 12 152 | block 7 13 153 | block 8 13 154 | block 9 13 155 | block 10 13 156 | block 11 13 157 | block 12 13 158 | block 13 13 159 | block 18 13 160 | block 19 13 161 | block 20 13 162 | block 21 13 163 | block 22 13 164 | block 23 13 165 | block 30 13 166 | block 31 13 167 | block 47 13 168 | block 48 13 169 | block 49 13 170 | block 50 13 171 | block 51 13 172 | block 52 13 173 | block 53 13 174 | block 54 13 175 | block 55 13 176 | block 56 13 177 | block 57 13 178 | block 58 13 179 | block 47 14 180 | block 48 14 181 | block 49 14 182 | block 50 14 183 | block 51 14 184 | block 52 14 185 | block 53 14 186 | block 54 14 187 | block 55 14 188 | block 56 14 189 | block 57 14 190 | block 58 14 191 | block 11 15 192 | block 12 15 193 | block 13 15 194 | block 14 15 195 | block 15 15 196 | block 47 15 197 | block 48 15 198 | block 49 15 199 | block 50 15 200 | block 51 15 201 | block 52 15 202 | block 53 15 203 | block 54 15 204 | block 55 15 205 | block 56 15 206 | block 57 15 207 | block 58 15 208 | block 11 16 209 | block 12 16 210 | block 13 16 211 | block 14 16 212 | block 15 16 213 | block 47 16 214 | block 48 16 215 | block 49 16 216 | block 50 16 217 | block 51 16 218 | block 52 16 219 | block 53 16 220 | block 55 16 221 | block 56 16 222 | block 57 16 223 | block 58 16 224 | block 11 17 225 | block 12 17 226 | block 13 17 227 | block 14 17 228 | block 15 17 229 | block 16 17 230 | block 17 17 231 | block 41 17 232 | block 42 17 233 | block 43 17 234 | block 55 17 235 | block 56 17 236 | block 57 17 237 | block 58 17 238 | block 11 18 239 | block 12 18 240 | block 13 18 241 | block 14 18 242 | block 15 18 243 | block 16 18 244 | block 17 18 245 | block 27 18 246 | block 28 18 247 | block 29 18 248 | block 30 18 249 | block 31 18 250 | block 32 18 251 | block 33 18 252 | block 41 18 253 | block 42 18 254 | block 43 18 255 | block 55 18 256 | block 56 18 257 | block 57 18 258 | block 58 18 259 | block 11 19 260 | block 12 19 261 | block 13 19 262 | block 14 19 263 | block 15 19 264 | block 16 19 265 | block 17 19 266 | block 27 19 267 | block 28 19 268 | block 29 19 269 | block 30 19 270 | block 31 19 271 | block 32 19 272 | block 33 19 273 | block 41 19 274 | block 42 19 275 | block 43 19 276 | block 55 19 277 | block 56 19 278 | block 57 19 279 | block 58 19 280 | block 11 20 281 | block 12 20 282 | block 13 20 283 | block 14 20 284 | block 15 20 285 | block 27 20 286 | block 28 20 287 | block 29 20 288 | block 30 20 289 | block 31 20 290 | block 32 20 291 | block 33 20 292 | block 41 20 293 | block 42 20 294 | block 43 20 295 | block 27 21 296 | block 28 21 297 | block 29 21 298 | block 30 21 299 | block 31 21 300 | block 32 21 301 | block 33 21 302 | block 41 21 303 | block 42 21 304 | block 43 21 305 | block 16 22 306 | block 17 22 307 | block 27 22 308 | block 28 22 309 | block 29 22 310 | block 30 22 311 | block 31 22 312 | block 32 22 313 | block 33 22 314 | block 16 23 315 | block 17 23 316 | block 27 23 317 | block 28 23 318 | block 29 23 319 | block 30 23 320 | block 31 23 321 | block 32 23 322 | block 33 23 323 | block 14 24 324 | block 15 24 325 | block 16 24 326 | block 17 24 327 | block 18 24 328 | block 19 24 329 | block 14 25 330 | block 15 25 331 | block 16 25 332 | block 17 25 333 | block 18 25 334 | block 19 25 335 | block 16 26 336 | block 17 26 337 | block 16 27 338 | block 17 27 339 | block 38 27 340 | block 39 27 341 | block 16 28 342 | block 17 28 343 | block 38 28 344 | block 39 28 345 | block 45 28 346 | block 46 28 347 | block 47 28 348 | block 16 29 349 | block 17 29 350 | block 18 29 351 | block 45 29 352 | block 46 29 353 | block 47 29 354 | block 6 30 355 | block 7 30 356 | block 8 30 357 | block 9 30 358 | block 10 30 359 | block 11 30 360 | block 16 30 361 | block 17 30 362 | block 18 30 363 | block 45 30 364 | block 46 30 365 | block 47 30 366 | block 6 31 367 | block 7 31 368 | block 8 31 369 | block 9 31 370 | block 10 31 371 | block 11 31 372 | block 16 31 373 | block 17 31 374 | block 18 31 375 | block 45 31 376 | block 46 31 377 | block 47 31 378 | block 16 32 379 | block 17 32 380 | block 18 32 381 | block 45 32 382 | block 46 32 383 | block 47 32 384 | spawn_position 44 8 385 | spawn_position 4 8 386 | spawn_position 9 16 387 | spawn_position 39 1 388 | spawn_position 23 19 389 | spawn_position 37 26 390 | -------------------------------------------------------------------------------- /maps/random_3_1.map: -------------------------------------------------------------------------------- 1 | map_size 70 30 2 | view_radius 12 3 | mining_radius 1 4 | attack_radius 4 5 | block 38 0 6 | block 39 0 7 | block 46 0 8 | block 47 0 9 | block 48 0 10 | block 52 0 11 | block 53 0 12 | block 54 0 13 | block 55 0 14 | block 56 0 15 | block 3 1 16 | block 4 1 17 | block 5 1 18 | block 6 1 19 | block 7 1 20 | block 38 1 21 | block 39 1 22 | block 46 1 23 | block 47 1 24 | block 48 1 25 | block 52 1 26 | block 53 1 27 | block 54 1 28 | block 55 1 29 | block 56 1 30 | block 3 2 31 | block 4 2 32 | block 5 2 33 | block 6 2 34 | block 7 2 35 | block 38 2 36 | block 39 2 37 | block 52 2 38 | block 53 2 39 | block 54 2 40 | block 55 2 41 | block 56 2 42 | block 3 3 43 | block 4 3 44 | block 5 3 45 | block 6 3 46 | block 7 3 47 | block 40 3 48 | block 41 3 49 | block 42 3 50 | block 43 3 51 | block 52 3 52 | block 53 3 53 | block 54 3 54 | block 55 3 55 | block 56 3 56 | block 3 4 57 | block 4 4 58 | block 5 4 59 | block 6 4 60 | block 7 4 61 | block 10 4 62 | block 11 4 63 | block 25 4 64 | block 26 4 65 | block 27 4 66 | block 28 4 67 | block 40 4 68 | block 41 4 69 | block 42 4 70 | block 43 4 71 | block 52 4 72 | block 53 4 73 | block 54 4 74 | block 55 4 75 | block 56 4 76 | block 3 5 77 | block 4 5 78 | block 5 5 79 | block 6 5 80 | block 7 5 81 | block 10 5 82 | block 11 5 83 | block 25 5 84 | block 26 5 85 | block 27 5 86 | block 28 5 87 | block 40 5 88 | block 41 5 89 | block 42 5 90 | block 43 5 91 | block 52 5 92 | block 53 5 93 | block 54 5 94 | block 55 5 95 | block 56 5 96 | block 10 6 97 | block 11 6 98 | block 25 6 99 | block 26 6 100 | block 27 6 101 | block 28 6 102 | block 40 6 103 | block 41 6 104 | block 42 6 105 | block 43 6 106 | block 46 6 107 | block 47 6 108 | block 48 6 109 | block 49 6 110 | block 50 6 111 | block 51 6 112 | block 52 6 113 | block 10 7 114 | block 11 7 115 | block 25 7 116 | block 26 7 117 | block 27 7 118 | block 28 7 119 | block 40 7 120 | block 41 7 121 | block 42 7 122 | block 43 7 123 | block 46 7 124 | block 47 7 125 | block 48 7 126 | block 49 7 127 | block 50 7 128 | block 51 7 129 | block 52 7 130 | block 10 8 131 | block 11 8 132 | block 25 8 133 | block 26 8 134 | block 27 8 135 | block 28 8 136 | block 40 8 137 | block 41 8 138 | block 42 8 139 | block 43 8 140 | block 46 8 141 | block 47 8 142 | block 48 8 143 | block 49 8 144 | block 50 8 145 | block 51 8 146 | block 52 8 147 | block 10 9 148 | block 11 9 149 | block 25 9 150 | block 26 9 151 | block 27 9 152 | block 28 9 153 | block 10 10 154 | block 11 10 155 | block 39 11 156 | block 40 11 157 | block 41 11 158 | block 42 11 159 | block 43 11 160 | block 39 12 161 | block 40 12 162 | block 41 12 163 | block 42 12 164 | block 43 12 165 | block 54 13 166 | block 55 13 167 | block 56 13 168 | block 57 13 169 | block 54 14 170 | block 55 14 171 | block 56 14 172 | block 57 14 173 | block 0 15 174 | block 24 15 175 | block 25 15 176 | block 26 15 177 | block 54 15 178 | block 55 15 179 | block 56 15 180 | block 57 15 181 | block 68 15 182 | block 69 15 183 | block 0 16 184 | block 24 16 185 | block 25 16 186 | block 26 16 187 | block 54 16 188 | block 55 16 189 | block 56 16 190 | block 57 16 191 | block 68 16 192 | block 69 16 193 | block 0 17 194 | block 24 17 195 | block 25 17 196 | block 26 17 197 | block 40 17 198 | block 41 17 199 | block 42 17 200 | block 43 17 201 | block 44 17 202 | block 54 17 203 | block 55 17 204 | block 56 17 205 | block 57 17 206 | block 68 17 207 | block 69 17 208 | block 0 18 209 | block 11 18 210 | block 12 18 211 | block 13 18 212 | block 24 18 213 | block 25 18 214 | block 26 18 215 | block 40 18 216 | block 41 18 217 | block 42 18 218 | block 43 18 219 | block 44 18 220 | block 54 18 221 | block 55 18 222 | block 56 18 223 | block 57 18 224 | block 58 18 225 | block 59 18 226 | block 60 18 227 | block 61 18 228 | block 68 18 229 | block 69 18 230 | block 0 19 231 | block 11 19 232 | block 12 19 233 | block 13 19 234 | block 24 19 235 | block 25 19 236 | block 26 19 237 | block 40 19 238 | block 41 19 239 | block 42 19 240 | block 43 19 241 | block 44 19 242 | block 57 19 243 | block 58 19 244 | block 59 19 245 | block 60 19 246 | block 61 19 247 | block 65 19 248 | block 66 19 249 | block 67 19 250 | block 68 19 251 | block 69 19 252 | block 0 20 253 | block 11 20 254 | block 12 20 255 | block 13 20 256 | block 24 20 257 | block 25 20 258 | block 26 20 259 | block 40 20 260 | block 41 20 261 | block 42 20 262 | block 43 20 263 | block 44 20 264 | block 65 20 265 | block 66 20 266 | block 67 20 267 | block 68 20 268 | block 69 20 269 | block 0 21 270 | block 1 21 271 | block 2 21 272 | block 3 21 273 | block 4 21 274 | block 5 21 275 | block 6 21 276 | block 11 21 277 | block 12 21 278 | block 13 21 279 | block 24 21 280 | block 25 21 281 | block 26 21 282 | block 40 21 283 | block 41 21 284 | block 42 21 285 | block 43 21 286 | block 44 21 287 | block 65 21 288 | block 66 21 289 | block 67 21 290 | block 68 21 291 | block 69 21 292 | block 1 22 293 | block 2 22 294 | block 3 22 295 | block 4 22 296 | block 5 22 297 | block 6 22 298 | block 11 22 299 | block 12 22 300 | block 13 22 301 | block 40 22 302 | block 41 22 303 | block 42 22 304 | block 43 22 305 | block 44 22 306 | block 65 22 307 | block 66 22 308 | block 67 22 309 | block 68 22 310 | block 1 23 311 | block 2 23 312 | block 3 23 313 | block 4 23 314 | block 5 23 315 | block 6 23 316 | block 1 24 317 | block 2 24 318 | block 3 24 319 | block 4 24 320 | block 5 24 321 | block 6 24 322 | block 59 24 323 | block 60 24 324 | block 61 24 325 | block 62 24 326 | block 63 24 327 | block 64 24 328 | block 1 25 329 | block 2 25 330 | block 3 25 331 | block 4 25 332 | block 5 25 333 | block 6 25 334 | block 21 25 335 | block 22 25 336 | block 23 25 337 | block 24 25 338 | block 25 25 339 | block 26 25 340 | block 27 25 341 | block 59 25 342 | block 60 25 343 | block 61 25 344 | block 62 25 345 | block 63 25 346 | block 64 25 347 | block 21 26 348 | block 22 26 349 | block 23 26 350 | block 24 26 351 | block 25 26 352 | block 26 26 353 | block 27 26 354 | block 59 26 355 | block 60 26 356 | block 61 26 357 | block 62 26 358 | block 63 26 359 | block 64 26 360 | block 21 27 361 | block 22 27 362 | block 23 27 363 | block 24 27 364 | block 25 27 365 | block 26 27 366 | block 27 27 367 | block 38 27 368 | block 39 27 369 | block 59 27 370 | block 60 27 371 | block 61 27 372 | block 62 27 373 | block 63 27 374 | block 64 27 375 | block 21 28 376 | block 22 28 377 | block 23 28 378 | block 24 28 379 | block 25 28 380 | block 26 28 381 | block 27 28 382 | block 38 28 383 | block 39 28 384 | block 59 28 385 | block 60 28 386 | block 61 28 387 | block 62 28 388 | block 63 28 389 | block 64 28 390 | block 38 29 391 | block 39 29 392 | block 46 29 393 | block 47 29 394 | block 48 29 395 | block 52 29 396 | block 53 29 397 | block 54 29 398 | block 55 29 399 | block 56 29 400 | spawn_position 33 1 401 | spawn_position 10 19 402 | spawn_position 19 5 403 | -------------------------------------------------------------------------------- /maps/random_4_1.map: -------------------------------------------------------------------------------- 1 | map_size 70 24 2 | view_radius 11 3 | mining_radius 1 4 | attack_radius 4 5 | block 5 0 6 | block 6 0 7 | block 7 0 8 | block 8 0 9 | block 9 0 10 | block 10 0 11 | block 11 0 12 | block 14 0 13 | block 15 0 14 | block 16 0 15 | block 17 0 16 | block 18 0 17 | block 21 0 18 | block 22 0 19 | block 23 0 20 | block 24 0 21 | block 25 0 22 | block 26 0 23 | block 39 0 24 | block 40 0 25 | block 41 0 26 | block 42 0 27 | block 43 0 28 | block 44 0 29 | block 45 0 30 | block 5 1 31 | block 6 1 32 | block 7 1 33 | block 8 1 34 | block 9 1 35 | block 10 1 36 | block 11 1 37 | block 14 1 38 | block 15 1 39 | block 16 1 40 | block 17 1 41 | block 18 1 42 | block 21 1 43 | block 22 1 44 | block 23 1 45 | block 47 1 46 | block 48 1 47 | block 49 1 48 | block 50 1 49 | block 64 1 50 | block 65 1 51 | block 66 1 52 | block 67 1 53 | block 68 1 54 | block 69 1 55 | block 0 2 56 | block 1 2 57 | block 2 2 58 | block 3 2 59 | block 4 2 60 | block 5 2 61 | block 6 2 62 | block 7 2 63 | block 8 2 64 | block 9 2 65 | block 10 2 66 | block 11 2 67 | block 14 2 68 | block 15 2 69 | block 16 2 70 | block 17 2 71 | block 18 2 72 | block 21 2 73 | block 22 2 74 | block 23 2 75 | block 39 2 76 | block 40 2 77 | block 41 2 78 | block 47 2 79 | block 48 2 80 | block 49 2 81 | block 50 2 82 | block 64 2 83 | block 65 2 84 | block 66 2 85 | block 67 2 86 | block 68 2 87 | block 69 2 88 | block 0 3 89 | block 1 3 90 | block 2 3 91 | block 3 3 92 | block 4 3 93 | block 5 3 94 | block 6 3 95 | block 7 3 96 | block 8 3 97 | block 9 3 98 | block 10 3 99 | block 11 3 100 | block 39 3 101 | block 40 3 102 | block 41 3 103 | block 47 3 104 | block 48 3 105 | block 49 3 106 | block 50 3 107 | block 64 3 108 | block 65 3 109 | block 66 3 110 | block 67 3 111 | block 68 3 112 | block 69 3 113 | block 0 4 114 | block 1 4 115 | block 2 4 116 | block 3 4 117 | block 4 4 118 | block 5 4 119 | block 6 4 120 | block 7 4 121 | block 8 4 122 | block 9 4 123 | block 10 4 124 | block 11 4 125 | block 39 4 126 | block 40 4 127 | block 41 4 128 | block 47 4 129 | block 48 4 130 | block 49 4 131 | block 50 4 132 | block 0 5 133 | block 1 5 134 | block 2 5 135 | block 3 5 136 | block 4 5 137 | block 39 5 138 | block 40 5 139 | block 41 5 140 | block 0 6 141 | block 1 6 142 | block 2 6 143 | block 3 6 144 | block 4 6 145 | block 39 6 146 | block 40 6 147 | block 41 6 148 | block 0 7 149 | block 1 7 150 | block 2 7 151 | block 3 7 152 | block 4 7 153 | block 13 7 154 | block 14 7 155 | block 15 7 156 | block 16 7 157 | block 17 7 158 | block 31 7 159 | block 32 7 160 | block 33 7 161 | block 0 8 162 | block 1 8 163 | block 2 8 164 | block 3 8 165 | block 4 8 166 | block 13 8 167 | block 14 8 168 | block 15 8 169 | block 16 8 170 | block 17 8 171 | block 31 8 172 | block 32 8 173 | block 33 8 174 | block 34 8 175 | block 35 8 176 | block 36 8 177 | block 67 8 178 | block 68 8 179 | block 69 8 180 | block 0 9 181 | block 1 9 182 | block 2 9 183 | block 3 9 184 | block 13 9 185 | block 14 9 186 | block 15 9 187 | block 16 9 188 | block 17 9 189 | block 18 9 190 | block 19 9 191 | block 31 9 192 | block 32 9 193 | block 33 9 194 | block 34 9 195 | block 35 9 196 | block 36 9 197 | block 67 9 198 | block 68 9 199 | block 69 9 200 | block 0 10 201 | block 1 10 202 | block 2 10 203 | block 3 10 204 | block 13 10 205 | block 14 10 206 | block 15 10 207 | block 16 10 208 | block 17 10 209 | block 18 10 210 | block 19 10 211 | block 31 10 212 | block 32 10 213 | block 33 10 214 | block 34 10 215 | block 35 10 216 | block 36 10 217 | block 67 10 218 | block 68 10 219 | block 69 10 220 | block 0 11 221 | block 1 11 222 | block 2 11 223 | block 3 11 224 | block 13 11 225 | block 14 11 226 | block 15 11 227 | block 16 11 228 | block 17 11 229 | block 18 11 230 | block 19 11 231 | block 20 11 232 | block 21 11 233 | block 25 11 234 | block 26 11 235 | block 27 11 236 | block 28 11 237 | block 29 11 238 | block 30 11 239 | block 34 11 240 | block 35 11 241 | block 36 11 242 | block 67 11 243 | block 68 11 244 | block 69 11 245 | block 0 12 246 | block 1 12 247 | block 2 12 248 | block 3 12 249 | block 13 12 250 | block 14 12 251 | block 15 12 252 | block 16 12 253 | block 17 12 254 | block 18 12 255 | block 19 12 256 | block 20 12 257 | block 21 12 258 | block 25 12 259 | block 26 12 260 | block 27 12 261 | block 28 12 262 | block 29 12 263 | block 30 12 264 | block 67 12 265 | block 68 12 266 | block 69 12 267 | block 0 13 268 | block 1 13 269 | block 2 13 270 | block 3 13 271 | block 13 13 272 | block 14 13 273 | block 15 13 274 | block 16 13 275 | block 17 13 276 | block 18 13 277 | block 19 13 278 | block 20 13 279 | block 21 13 280 | block 22 13 281 | block 25 13 282 | block 26 13 283 | block 27 13 284 | block 28 13 285 | block 29 13 286 | block 30 13 287 | block 67 13 288 | block 68 13 289 | block 69 13 290 | block 14 14 291 | block 15 14 292 | block 16 14 293 | block 17 14 294 | block 18 14 295 | block 19 14 296 | block 20 14 297 | block 21 14 298 | block 22 14 299 | block 46 14 300 | block 47 14 301 | block 14 15 302 | block 15 15 303 | block 16 15 304 | block 17 15 305 | block 18 15 306 | block 19 15 307 | block 20 15 308 | block 21 15 309 | block 22 15 310 | block 26 15 311 | block 27 15 312 | block 28 15 313 | block 29 15 314 | block 30 15 315 | block 31 15 316 | block 32 15 317 | block 46 15 318 | block 47 15 319 | block 20 16 320 | block 21 16 321 | block 22 16 322 | block 26 16 323 | block 27 16 324 | block 28 16 325 | block 29 16 326 | block 30 16 327 | block 31 16 328 | block 32 16 329 | block 26 17 330 | block 27 17 331 | block 28 17 332 | block 29 17 333 | block 30 17 334 | block 31 17 335 | block 32 17 336 | block 24 18 337 | block 25 18 338 | block 26 18 339 | block 27 18 340 | block 28 18 341 | block 29 18 342 | block 30 18 343 | block 31 18 344 | block 32 18 345 | block 60 18 346 | block 61 18 347 | block 62 18 348 | block 63 18 349 | block 64 18 350 | block 18 19 351 | block 19 19 352 | block 24 19 353 | block 25 19 354 | block 26 19 355 | block 27 19 356 | block 28 19 357 | block 29 19 358 | block 30 19 359 | block 31 19 360 | block 32 19 361 | block 60 19 362 | block 61 19 363 | block 62 19 364 | block 63 19 365 | block 64 19 366 | block 18 20 367 | block 19 20 368 | block 21 20 369 | block 22 20 370 | block 23 20 371 | block 24 20 372 | block 25 20 373 | block 26 20 374 | block 27 20 375 | block 28 20 376 | block 29 20 377 | block 30 20 378 | block 31 20 379 | block 32 20 380 | block 41 20 381 | block 42 20 382 | block 18 21 383 | block 19 21 384 | block 21 21 385 | block 22 21 386 | block 23 21 387 | block 24 21 388 | block 25 21 389 | block 26 21 390 | block 40 21 391 | block 41 21 392 | block 42 21 393 | block 43 21 394 | block 44 21 395 | block 5 22 396 | block 6 22 397 | block 7 22 398 | block 8 22 399 | block 9 22 400 | block 10 22 401 | block 11 22 402 | block 21 22 403 | block 22 22 404 | block 23 22 405 | block 24 22 406 | block 25 22 407 | block 26 22 408 | block 39 22 409 | block 40 22 410 | block 41 22 411 | block 42 22 412 | block 43 22 413 | block 44 22 414 | block 45 22 415 | block 5 23 416 | block 6 23 417 | block 7 23 418 | block 8 23 419 | block 9 23 420 | block 10 23 421 | block 11 23 422 | block 21 23 423 | block 22 23 424 | block 23 23 425 | block 24 23 426 | block 25 23 427 | block 26 23 428 | block 39 23 429 | block 40 23 430 | block 41 23 431 | block 42 23 432 | block 43 23 433 | block 44 23 434 | block 45 23 435 | spawn_position 47 17 436 | spawn_position 55 12 437 | spawn_position 68 23 438 | spawn_position 40 7 439 | -------------------------------------------------------------------------------- /maps/map_2_2.map: -------------------------------------------------------------------------------- 1 | map_size 25 48 2 | view_radius 9 3 | mining_radius 2 4 | attack_radius 3 5 | block 24 10 6 | block 24 11 7 | block 24 12 8 | block 24 13 9 | block 24 14 10 | block 24 15 11 | block 24 16 12 | block 24 17 13 | block 24 21 14 | block 24 22 15 | block 24 25 16 | block 24 26 17 | block 24 30 18 | block 24 31 19 | block 24 32 20 | block 24 33 21 | block 24 34 22 | block 24 35 23 | block 24 36 24 | block 24 37 25 | block 23 2 26 | block 23 8 27 | block 23 9 28 | block 23 10 29 | block 23 11 30 | block 23 12 31 | block 23 13 32 | block 23 14 33 | block 23 15 34 | block 23 16 35 | block 23 17 36 | block 23 20 37 | block 23 21 38 | block 23 22 39 | block 23 25 40 | block 23 26 41 | block 23 27 42 | block 23 30 43 | block 23 31 44 | block 23 32 45 | block 23 33 46 | block 23 34 47 | block 23 35 48 | block 23 36 49 | block 23 37 50 | block 23 38 51 | block 23 39 52 | block 23 45 53 | block 22 1 54 | block 22 2 55 | block 22 3 56 | block 22 8 57 | block 22 9 58 | block 22 10 59 | block 22 11 60 | block 22 15 61 | block 22 16 62 | block 22 19 63 | block 22 20 64 | block 22 21 65 | block 22 22 66 | block 22 25 67 | block 22 26 68 | block 22 27 69 | block 22 28 70 | block 22 31 71 | block 22 32 72 | block 22 36 73 | block 22 37 74 | block 22 38 75 | block 22 39 76 | block 22 44 77 | block 22 45 78 | block 22 46 79 | block 21 1 80 | block 21 2 81 | block 21 3 82 | block 21 4 83 | block 21 18 84 | block 21 19 85 | block 21 20 86 | block 21 21 87 | block 21 26 88 | block 21 27 89 | block 21 28 90 | block 21 29 91 | block 21 43 92 | block 21 44 93 | block 21 45 94 | block 21 46 95 | block 20 1 96 | block 20 2 97 | block 20 3 98 | block 20 4 99 | block 20 17 100 | block 20 18 101 | block 20 19 102 | block 20 20 103 | block 20 21 104 | block 20 22 105 | block 20 25 106 | block 20 26 107 | block 20 27 108 | block 20 28 109 | block 20 29 110 | block 20 30 111 | block 20 43 112 | block 20 44 113 | block 20 45 114 | block 20 46 115 | block 19 2 116 | block 19 3 117 | block 19 4 118 | block 19 9 119 | block 19 10 120 | block 19 11 121 | block 19 17 122 | block 19 18 123 | block 19 19 124 | block 19 20 125 | block 19 21 126 | block 19 26 127 | block 19 27 128 | block 19 28 129 | block 19 29 130 | block 19 30 131 | block 19 36 132 | block 19 37 133 | block 19 38 134 | block 19 43 135 | block 19 44 136 | block 19 45 137 | block 18 1 138 | block 18 2 139 | block 18 3 140 | block 18 4 141 | block 18 9 142 | block 18 10 143 | block 18 17 144 | block 18 18 145 | block 18 29 146 | block 18 30 147 | block 18 37 148 | block 18 38 149 | block 18 43 150 | block 18 44 151 | block 18 45 152 | block 18 46 153 | block 17 1 154 | block 17 2 155 | block 17 3 156 | block 17 9 157 | block 17 10 158 | block 17 11 159 | block 17 18 160 | block 17 29 161 | block 17 36 162 | block 17 37 163 | block 17 38 164 | block 17 44 165 | block 17 45 166 | block 17 46 167 | block 16 1 168 | block 16 2 169 | block 16 3 170 | block 16 9 171 | block 16 10 172 | block 16 11 173 | block 16 12 174 | block 16 15 175 | block 16 16 176 | block 16 17 177 | block 16 18 178 | block 16 19 179 | block 16 28 180 | block 16 29 181 | block 16 30 182 | block 16 31 183 | block 16 32 184 | block 16 35 185 | block 16 36 186 | block 16 37 187 | block 16 38 188 | block 16 44 189 | block 16 45 190 | block 16 46 191 | block 15 1 192 | block 15 2 193 | block 15 3 194 | block 15 10 195 | block 15 11 196 | block 15 12 197 | block 15 15 198 | block 15 16 199 | block 15 17 200 | block 15 18 201 | block 15 19 202 | block 15 28 203 | block 15 29 204 | block 15 30 205 | block 15 31 206 | block 15 32 207 | block 15 35 208 | block 15 36 209 | block 15 37 210 | block 15 44 211 | block 15 45 212 | block 15 46 213 | block 14 1 214 | block 14 2 215 | block 14 9 216 | block 14 10 217 | block 14 15 218 | block 14 16 219 | block 14 31 220 | block 14 32 221 | block 14 37 222 | block 14 38 223 | block 14 45 224 | block 14 46 225 | block 13 9 226 | block 13 10 227 | block 13 37 228 | block 13 38 229 | block 11 9 230 | block 11 10 231 | block 11 37 232 | block 11 38 233 | block 10 1 234 | block 10 2 235 | block 10 9 236 | block 10 10 237 | block 10 15 238 | block 10 16 239 | block 10 31 240 | block 10 32 241 | block 10 37 242 | block 10 38 243 | block 10 45 244 | block 10 46 245 | block 9 1 246 | block 9 2 247 | block 9 3 248 | block 9 10 249 | block 9 11 250 | block 9 12 251 | block 9 15 252 | block 9 16 253 | block 9 17 254 | block 9 18 255 | block 9 19 256 | block 9 28 257 | block 9 29 258 | block 9 30 259 | block 9 31 260 | block 9 32 261 | block 9 35 262 | block 9 36 263 | block 9 37 264 | block 9 44 265 | block 9 45 266 | block 9 46 267 | block 8 1 268 | block 8 2 269 | block 8 3 270 | block 8 9 271 | block 8 10 272 | block 8 11 273 | block 8 12 274 | block 8 15 275 | block 8 16 276 | block 8 17 277 | block 8 18 278 | block 8 19 279 | block 8 28 280 | block 8 29 281 | block 8 30 282 | block 8 31 283 | block 8 32 284 | block 8 35 285 | block 8 36 286 | block 8 37 287 | block 8 38 288 | block 8 44 289 | block 8 45 290 | block 8 46 291 | block 7 1 292 | block 7 2 293 | block 7 3 294 | block 7 9 295 | block 7 10 296 | block 7 11 297 | block 7 18 298 | block 7 29 299 | block 7 36 300 | block 7 37 301 | block 7 38 302 | block 7 44 303 | block 7 45 304 | block 7 46 305 | block 6 1 306 | block 6 2 307 | block 6 3 308 | block 6 4 309 | block 6 9 310 | block 6 10 311 | block 6 17 312 | block 6 18 313 | block 6 29 314 | block 6 30 315 | block 6 37 316 | block 6 38 317 | block 6 43 318 | block 6 44 319 | block 6 45 320 | block 6 46 321 | block 5 2 322 | block 5 3 323 | block 5 4 324 | block 5 9 325 | block 5 10 326 | block 5 11 327 | block 5 17 328 | block 5 18 329 | block 5 19 330 | block 5 20 331 | block 5 21 332 | block 5 26 333 | block 5 27 334 | block 5 28 335 | block 5 29 336 | block 5 30 337 | block 5 36 338 | block 5 37 339 | block 5 38 340 | block 5 43 341 | block 5 44 342 | block 5 45 343 | block 4 1 344 | block 4 2 345 | block 4 3 346 | block 4 4 347 | block 4 17 348 | block 4 18 349 | block 4 19 350 | block 4 20 351 | block 4 21 352 | block 4 22 353 | block 4 25 354 | block 4 26 355 | block 4 27 356 | block 4 28 357 | block 4 29 358 | block 4 30 359 | block 4 43 360 | block 4 44 361 | block 4 45 362 | block 4 46 363 | block 3 1 364 | block 3 2 365 | block 3 3 366 | block 3 4 367 | block 3 18 368 | block 3 19 369 | block 3 20 370 | block 3 21 371 | block 3 26 372 | block 3 27 373 | block 3 28 374 | block 3 29 375 | block 3 43 376 | block 3 44 377 | block 3 45 378 | block 3 46 379 | block 2 1 380 | block 2 2 381 | block 2 3 382 | block 2 8 383 | block 2 9 384 | block 2 10 385 | block 2 11 386 | block 2 15 387 | block 2 16 388 | block 2 19 389 | block 2 20 390 | block 2 21 391 | block 2 22 392 | block 2 25 393 | block 2 26 394 | block 2 27 395 | block 2 28 396 | block 2 31 397 | block 2 32 398 | block 2 36 399 | block 2 37 400 | block 2 38 401 | block 2 39 402 | block 2 44 403 | block 2 45 404 | block 2 46 405 | block 1 2 406 | block 1 8 407 | block 1 9 408 | block 1 10 409 | block 1 11 410 | block 1 12 411 | block 1 13 412 | block 1 14 413 | block 1 15 414 | block 1 16 415 | block 1 17 416 | block 1 20 417 | block 1 21 418 | block 1 22 419 | block 1 25 420 | block 1 26 421 | block 1 27 422 | block 1 30 423 | block 1 31 424 | block 1 32 425 | block 1 33 426 | block 1 34 427 | block 1 35 428 | block 1 36 429 | block 1 37 430 | block 1 38 431 | block 1 39 432 | block 1 45 433 | block 0 10 434 | block 0 11 435 | block 0 12 436 | block 0 13 437 | block 0 14 438 | block 0 15 439 | block 0 16 440 | block 0 17 441 | block 0 21 442 | block 0 22 443 | block 0 25 444 | block 0 26 445 | block 0 30 446 | block 0 31 447 | block 0 32 448 | block 0 33 449 | block 0 34 450 | block 0 35 451 | block 0 36 452 | block 0 37 453 | spawn_position 20 38 454 | spawn_position 4 9 455 | --------------------------------------------------------------------------------