From cfc330ed7a63f71170f322c38a7ad9e07208b41d Mon Sep 17 00:00:00 2001 From: Bharat Reddy Date: Sat, 29 Feb 2020 18:33:36 +0000 Subject: [PATCH] Use relative and modular imports --- Pipfile | 1 + Pipfile.lock | 26 ++++++++++++- README.md | 6 ++- __init__.py | 5 +++ main_package/actions.py => actions.py | 0 entities/__init__.py | 4 ++ .../fieldEntities => entities}/ant.py | 7 +--- .../interfaces => entities}/attackable.py | 0 .../fieldEntities => entities}/base.py | 4 +- .../fieldEntities => entities}/food.py | 4 -- main_package/field.py => field.py | 20 +++++----- main_package/gameBoard.py => gameBoard.py | 31 +++++++-------- main_package/gameServer.py => gameServer.py | 3 +- main_package/main.py => main.py | 4 +- main_package/fieldEntities/__init__.py | 0 main_package/interfaces/__init__.py | 0 .../mapGenerator.py => mapGenerator.py | 22 +++++------ {main_package => tests}/__init__.py | 0 {main_package => tests}/test_gameBoard.py | 39 ++++++++----------- {main_package => tests}/test_gameServer.py | 8 +--- 20 files changed, 99 insertions(+), 85 deletions(-) create mode 100644 __init__.py rename main_package/actions.py => actions.py (100%) create mode 100644 entities/__init__.py rename {main_package/fieldEntities => entities}/ant.py (86%) rename {main_package/interfaces => entities}/attackable.py (100%) rename {main_package/fieldEntities => entities}/base.py (55%) rename {main_package/fieldEntities => entities}/food.py (69%) rename main_package/field.py => field.py (74%) rename main_package/gameBoard.py => gameBoard.py (92%) rename main_package/gameServer.py => gameServer.py (97%) rename main_package/main.py => main.py (57%) delete mode 100644 main_package/fieldEntities/__init__.py delete mode 100644 main_package/interfaces/__init__.py rename main_package/mapGenerator.py => mapGenerator.py (84%) rename {main_package => tests}/__init__.py (100%) rename {main_package => tests}/test_gameBoard.py (89%) rename {main_package => tests}/test_gameServer.py (93%) diff --git a/Pipfile b/Pipfile index dd56b2a..e760bcd 100644 --- a/Pipfile +++ b/Pipfile @@ -9,6 +9,7 @@ pytest = "*" [packages] opensimplex = "*" colorama = "*" +atomicwrites = "*" [requires] python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock index ca07f09..23ab764 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "2c3d7250907269b75745b5c47faafa6dfb42d0807d2964e8ee1da5bddf20c665" + "sha256": "91d9e240636a6fac307fcd0cbda5a59543f9998d7351221a6d6c33810ef2bee7" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,14 @@ ] }, "default": { + "atomicwrites": { + "hashes": [ + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + ], + "index": "pypi", + "version": "==1.3.0" + }, "colorama": { "hashes": [ "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", @@ -33,6 +41,14 @@ } }, "develop": { + "atomicwrites": { + "hashes": [ + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + ], + "index": "pypi", + "version": "==1.3.0" + }, "attrs": { "hashes": [ "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", @@ -40,6 +56,14 @@ ], "version": "==19.3.0" }, + "colorama": { + "hashes": [ + "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", + "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" + ], + "index": "pypi", + "version": "==0.4.3" + }, "more-itertools": { "hashes": [ "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c", diff --git a/README.md b/README.md index 7627be9..c2480b7 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ ``` PIPENV_VENV_IN_PROJECT=1 -pipenv install +pipenv sync + +pipenv run + +pipenv shell ``` diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..31b430a --- /dev/null +++ b/__init__.py @@ -0,0 +1,5 @@ +from .actions import Actions +from .entities import Ant, Base, Food +from .field import Field, FieldType +from .gameBoard import GameBoard +from .gameServer import Server \ No newline at end of file diff --git a/main_package/actions.py b/actions.py similarity index 100% rename from main_package/actions.py rename to actions.py diff --git a/entities/__init__.py b/entities/__init__.py new file mode 100644 index 0000000..df4f2bf --- /dev/null +++ b/entities/__init__.py @@ -0,0 +1,4 @@ +from .ant import Ant +from .attackable import Attackable +from .base import Base +from .food import Food \ No newline at end of file diff --git a/main_package/fieldEntities/ant.py b/entities/ant.py similarity index 86% rename from main_package/fieldEntities/ant.py rename to entities/ant.py index 7bc85bb..d896808 100644 --- a/main_package/fieldEntities/ant.py +++ b/entities/ant.py @@ -1,7 +1,5 @@ -import main_package.field as field -from main_package.fieldEntities.food import Food -from main_package.interfaces.attackable import Attackable - +from .attackable import Attackable +from .food import Food class Ant(Attackable): @@ -14,7 +12,6 @@ def __init__(self, antId: str, playerName: str): self.attackDamage: int = 1 # the damage the ant inflicts per attack self._foodUptakeSpeed: int = 2 # how much food the ant acquires per feed action self.movementEfficiency: int = 2 # TODO how many fields the ant can move before using 1 food, not yet implemented - self.fieldPosition: field.Field = None def getFoodUptakeValue(self): """ diff --git a/main_package/interfaces/attackable.py b/entities/attackable.py similarity index 100% rename from main_package/interfaces/attackable.py rename to entities/attackable.py diff --git a/main_package/fieldEntities/base.py b/entities/base.py similarity index 55% rename from main_package/fieldEntities/base.py rename to entities/base.py index a404aa4..6e2a65e 100644 --- a/main_package/fieldEntities/base.py +++ b/entities/base.py @@ -1,11 +1,9 @@ -import main_package.field as field -from main_package.interfaces.attackable import Attackable +from .attackable import Attackable class Base(Attackable): def __init__(self, player: str): Attackable.__init__(self, 100) - self.fieldPosition: field.Field = None self.player = player self.health = 100 # placeholder diff --git a/main_package/fieldEntities/food.py b/entities/food.py similarity index 69% rename from main_package/fieldEntities/food.py rename to entities/food.py index d000f95..596fe3d 100644 --- a/main_package/fieldEntities/food.py +++ b/entities/food.py @@ -1,17 +1,13 @@ -import main_package.field as field class Food: def __init__(self, magnitude: int): self.foodMagnitude = magnitude - self.fieldPosition: field.Field = None def getFood(self, magnitude) -> int: if magnitude >= self.foodMagnitude: returnVal = self.foodMagnitude self.foodMagnitude = 0 - self.fieldPosition.resetToEmpty() - self.fieldPosition = None return returnVal else: self.foodMagnitude -= magnitude diff --git a/main_package/field.py b/field.py similarity index 74% rename from main_package/field.py rename to field.py index b40ae8c..5bf9ccb 100644 --- a/main_package/field.py +++ b/field.py @@ -2,15 +2,13 @@ import logging from typing import Tuple -import main_package.fieldEntities.ant as ant -import main_package.fieldEntities.food as food -import main_package.fieldEntities.base as base +from hive import Ant, Base, Food from colorama import init, Fore init() logging.basicConfig(level=logging.INFO) -class FieldTypeEnum(Enum): +class FieldType(Enum): BASE = Fore.RED + "B" ANT = Fore.BLUE + "A" FOOD = Fore.GREEN + "F" @@ -27,7 +25,7 @@ class FieldTypeEnum(Enum): class Field: log = logging.getLogger(__name__) - def __init__(self, xpos:int, ypos:int, type: FieldTypeEnum): + def __init__(self, xpos:int, ypos:int, type: FieldType): self.xpos = xpos self.ypos = ypos self.emptyType = type @@ -45,12 +43,12 @@ def setEntity(self, entity) -> bool: if self.entity is not None: self.log.error("Cannot set entity on field that is {} instead of empty".format(self.type)) return False - if isinstance(entity, ant.Ant): - self.type = FieldTypeEnum.ANT - elif isinstance(entity, food.Food): - self.type = FieldTypeEnum.FOOD - elif isinstance(entity, base.Base): - self.type = FieldTypeEnum.BASE + if isinstance(entity, Ant): + self.type = FieldType.ANT + elif isinstance(entity, Food): + self.type = FieldType.FOOD + elif isinstance(entity, Base): + self.type = FieldType.BASE else: self.log.error("Entity is of unknown type. Cannot set field type.") return False diff --git a/main_package/gameBoard.py b/gameBoard.py similarity index 92% rename from main_package/gameBoard.py rename to gameBoard.py index d83bd83..d3d5a7f 100644 --- a/main_package/gameBoard.py +++ b/gameBoard.py @@ -1,17 +1,16 @@ from typing import Tuple, List, Set -from main_package.fieldEntities.ant import Ant -from main_package.field import * -from main_package.mapGenerator import * -from main_package.fieldEntities.base import Base -from main_package.fieldEntities.food import Food -from main_package.interfaces.attackable import Attackable +from hive.entities import Attackable, Ant, Base, Food +from .field import Field, FieldType +from .mapGenerator import MapGenerator + +import logging logging.basicConfig(level=logging.INFO) -class gameBoard: +class GameBoard: log = logging.getLogger(__name__) - validForAttack = [FieldTypeEnum.ANT, FieldTypeEnum.BASE] + validForAttack = [FieldType.ANT, FieldType.BASE] def __init__(self, xdim: int = 50, ydim: int = 40): """ @@ -52,7 +51,7 @@ def createBase(self, xpos: int, ypos: int, player: str) -> bool: # field where base is placed must be empty field = self.getField(xpos, ypos) - if field.type != FieldTypeEnum.GRASS: + if field.type != FieldType.GRASS: logging.error("Base cannot be placed on field that is not grass. Field is {}".format(field.type)) return False @@ -130,15 +129,15 @@ def createAnt(self, xpos: int, ypos: int, antId: str, player: str) -> bool: # placement checks placementDesitnation: Field = self.getField(xpos, ypos) neighbouring_fields: List[Field] = self.getNeighbouringFields(placementDesitnation) - if not any(f.type == FieldTypeEnum.BASE for f in neighbouring_fields): + if not any(f.type == FieldType.BASE for f in neighbouring_fields): self.log.error("Invalid Placement, no adjacent base") return False - elif placementDesitnation.type is not FieldTypeEnum.GRASS: + elif placementDesitnation.type is not FieldType.GRASS: self.log.error("Invalid Placement, field not grass") return False # check if player owns base near which they want to place ant - base: Base = next(filter(lambda x: x.type == FieldTypeEnum.BASE, neighbouring_fields)).entity + base: Base = next(filter(lambda x: x.type == FieldType.BASE, neighbouring_fields)).entity if base.player != player: self.log.error("Player {} does not own the adjacent base".format(player)) return False @@ -159,7 +158,7 @@ def moveAnt(self, antId: str, xpos: int, ypos: int) -> bool: ant = self.ants[antId] # determine valid fields for movement fields = self.getNeighbouringFields(ant.fieldPosition) - validFields = filter(lambda x: x.type != FieldTypeEnum.ROCK, fields) + validFields = filter(lambda x: x.type != FieldType.ROCK, fields) # is movement valid ? fieldToMoveTo: Field = None @@ -192,7 +191,7 @@ def attack(self, antId: str, xpos: int, ypos: int) -> bool: if fieldToAttack not in neighbouringFields: self.log.error("The field {} is not in range of ant {}".format((xpos, ypos), antId)) return False - if fieldToAttack.type not in gameBoard.validForAttack: + if fieldToAttack.type not in GameBoard.validForAttack: self.log.error("The field {} is not a valid attack target for ant {}".format((xpos, ypos), antId)) return False target: Attackable = fieldToAttack.entity # TODO: might need "attackable" interface later @@ -234,7 +233,7 @@ def getBase(self, playerName: str) -> Base or None: def createFood(self, xpos: int, ypos: int, magnitude: int) -> bool: targetField = self.getField(xpos, ypos) - if targetField is None or targetField.type is not FieldTypeEnum.GRASS: + if targetField is None or targetField.type is not FieldType.GRASS: self.log.error("Invalid target ({},{}) for placing food.".format(xpos, ypos)) return False if magnitude <= 0 or magnitude != magnitude: # test for negative or nan @@ -256,7 +255,7 @@ def feed(self, antId: str, targetXpos: int, targetYpos: int) -> bool: self.log.error("Ant with id {} is not in range of targeted field ({},{})".format(antId, targetXpos, targetYpos)) return False # check if field is food - if targetField.type != FieldTypeEnum.FOOD: + if targetField.type != FieldType.FOOD: self.log.error("Ant with id {} tried to feed on a non food field ({},{})".format(antId,targetXpos,targetYpos)) return False # ants have a food capacity and feeding speed value diff --git a/main_package/gameServer.py b/gameServer.py similarity index 97% rename from main_package/gameServer.py rename to gameServer.py index ebe0b98..474f02e 100644 --- a/main_package/gameServer.py +++ b/gameServer.py @@ -1,8 +1,7 @@ import json import uuid -from main_package.actions import Actions - +from .actions import Actions class Server: diff --git a/main_package/main.py b/main.py similarity index 57% rename from main_package/main.py rename to main.py index 39f6f28..afd5dac 100644 --- a/main_package/main.py +++ b/main.py @@ -1,7 +1,7 @@ -from main_package.gameBoard import gameBoard +from .world import GameBoard import sys if __name__ == '__main__': print(sys.path) - server = gameBoard() + server = GameBoard() print(server.getBoardString()) \ No newline at end of file diff --git a/main_package/fieldEntities/__init__.py b/main_package/fieldEntities/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/main_package/interfaces/__init__.py b/main_package/interfaces/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/main_package/mapGenerator.py b/mapGenerator.py similarity index 84% rename from main_package/mapGenerator.py rename to mapGenerator.py index 09cacfd..4dfc33b 100644 --- a/main_package/mapGenerator.py +++ b/mapGenerator.py @@ -1,4 +1,4 @@ -from main_package.field import * +from .field import Field, FieldType from opensimplex import OpenSimplex class MapGenerator: @@ -30,30 +30,30 @@ def __init__(self, width: int, height: int): self.noiseGenerator = OpenSimplex() self.map = self.createMap() - def getTerrain(self, elevation: int, moisture: int) -> FieldTypeEnum: + def getTerrain(self, elevation: int, moisture: int) -> FieldType: e = elevation * 100 # elevation [0, 100] m = moisture * 100 # moisture [0, 100] if (e < self.waterMaxElevation / 3): - return FieldTypeEnum.DEEP_WATER + return FieldType.DEEP_WATER if (e < self.waterMaxElevation): - return FieldTypeEnum.WATER + return FieldType.WATER if (e < self.waterMaxElevation + self.sandMaxElevation): - return FieldTypeEnum.SAND + return FieldType.SAND if (e > self.rockMinElevation): - return FieldTypeEnum.ROCK + return FieldType.ROCK if (e > self.rockMinElevation - self.sandMaxElevation): - return FieldTypeEnum.TALL_GRASS + return FieldType.TALL_GRASS if (m < self.grassMinMoisture): - return FieldTypeEnum.DRY_GRASS + return FieldType.DRY_GRASS if (e < self.forestMaxElevation and m > self.forestMinMoisture and m < self.forestMaxMoisture): - return FieldTypeEnum.FOREST + return FieldType.FOREST if (e > self.tallGrassMinElevation and m > self.tallGrassMinMoisture): - return FieldTypeEnum.TALL_GRASS + return FieldType.TALL_GRASS - return FieldTypeEnum.GRASS + return FieldType.GRASS def createMap(self): map = [] diff --git a/main_package/__init__.py b/tests/__init__.py similarity index 100% rename from main_package/__init__.py rename to tests/__init__.py diff --git a/main_package/test_gameBoard.py b/tests/test_gameBoard.py similarity index 89% rename from main_package/test_gameBoard.py rename to tests/test_gameBoard.py index 2a470d4..9c9a1ff 100644 --- a/main_package/test_gameBoard.py +++ b/tests/test_gameBoard.py @@ -1,18 +1,13 @@ from typing import List from unittest import TestCase -from main_package.fieldEntities.base import Base -from main_package.gameBoard import gameBoard -from main_package.fieldEntities.ant import Ant -from main_package.field import FieldTypeEnum, Field -from main_package.fieldEntities.food import Food +from hive import Ant, Base, Food, FieldType, Field, GameBoard - -class TestgameBoard(TestCase): +class TestGameBoard(TestCase): def test_fogOfWar(self): # setup - board = gameBoard(6, 6) + board = GameBoard(6, 6) board.createBase(1,1,"player1") board.createBase(4,4,"player2") board.createAnt(2,1,"a","player1") @@ -45,7 +40,7 @@ def test_fogOfWar(self): self.assertTrue(all(visible.getPos() in expectedVisibleFieldsP1 for visible in visibleFieldsP1)) def test_ant_creation(self): - board = gameBoard(10, 10) + board = GameBoard(10, 10) board.createBase(2, 2, "testPlayer") # cant create ant with invalid player name self.assertFalse(board.createAnt(1, 1, "someAnt", "otherPlayer")) @@ -60,7 +55,7 @@ def test_ant_creation(self): self.assertFalse(board.createAnt(1, 2, "A", "testPlayer")) def test_base_creation(self): - board = gameBoard(10, 10) + board = GameBoard(10, 10) self.assertTrue(board.createBase(2, 2, "testPlayer")) self.assertFalse(board.createBase(2, 2, "testPlayer2")) # base already exists at same coords self.assertFalse(board.createBase(15, 15, "testPlayer3")) # outside board @@ -68,7 +63,7 @@ def test_base_creation(self): self.assertFalse(board.createBase(3, 3, "testPlayer")) # valid coords but 'testPlayer' already has a base def test_get_neighbouring_field_coordinates(self): - board = gameBoard(3, 3) + board = GameBoard(3, 3) coords = board.getNeighbouringFieldCoordinates(1, 1) # middle of board self.assertTrue(len(coords) == 8) for coordinate in [(0, 0), (0, 1), (0, 2), (0, 1), (2, 1), (0, 2), (1, 2), (2, 2)]: @@ -80,7 +75,7 @@ def test_get_neighbouring_field_coordinates(self): self.assertTrue(coordinate in coords) def test_ant_movement(self): - board = gameBoard(4, 4) + board = GameBoard(4, 4) board.createBase(1, 1, "testPlayer") board.createAnt(0, 0, "A", "testPlayer") board.createAnt(0, 1, "B", "testPlayer") @@ -95,7 +90,7 @@ def test_ant_movement(self): self.assertFalse(board.moveAnt("C", 1, 3)) # moving ant that does not exist def test_ant_attack(self): - board = gameBoard(3, 3) + board = GameBoard(3, 3) board.createBase(1, 1, "testPlayer") board.createAnt(0, 0, "A", "testPlayer") board.createAnt(0, 1, "B", "testPlayer") @@ -110,36 +105,36 @@ def test_ant_attack(self): antA.attackDamage = 5 antB: Ant = board.getAnt("B") antBField = antB.fieldPosition - self.assertTrue(antBField.type == FieldTypeEnum.ANT) + self.assertTrue(antBField.type == FieldType.ANT) antB.health = 10 self.assertTrue(board.attack("A", 0, 1)) self.assertTrue(antB.health == 5) board.tick() - self.assertTrue(antBField.type == FieldTypeEnum.ANT) + self.assertTrue(antBField.type == FieldType.ANT) self.assertTrue(board.getAnt("B") is not None) # ant C damaged but alive self.assertTrue(board.attack("A", 0, 1)) self.assertTrue(antB.health == 0) board.tick() - self.assertTrue(antBField.type == FieldTypeEnum.EMPTY) + self.assertTrue(antBField.type == FieldType.EMPTY) self.assertTrue(board.getAnt("B") is None) # dead ants removed from board # test attacking and killing base base: Base = board.getBase("testPlayer") baseField = base.fieldPosition - self.assertTrue(baseField.type == FieldTypeEnum.BASE) + self.assertTrue(baseField.type == FieldType.BASE) base.health = 10 self.assertTrue(board.attack("A", 1, 1)) board.tick() self.assertTrue(base.health == 5) self.assertTrue(board.attack("A", 1, 1)) board.tick() - self.assertTrue(baseField.type == FieldTypeEnum.EMPTY) + self.assertTrue(baseField.type == FieldType.EMPTY) self.assertTrue(board.getBase("testPlayer") is None) base.health = 0 def test_ant_feed(self): - board = gameBoard(5, 5) + board = GameBoard(5, 5) board.createBase(1, 1, "testPlayer") board.createAnt(0, 1, "A", "testPlayer") antA: Ant = board.getAnt("A") @@ -148,7 +143,7 @@ def test_ant_feed(self): antC: Ant = board.getAnt("C") antB: Ant = board.getAnt("B") self.assertTrue(board.createFood(0, 3, 11)) - self.assertTrue(board.getField(0, 3).type == FieldTypeEnum.FOOD) + self.assertTrue(board.getField(0, 3).type == FieldType.FOOD) # feeding out of range food: Food = board.getField(0, 3).entity self.assertFalse(board.feed("A", 0, 3)) @@ -170,10 +165,10 @@ def test_ant_feed(self): self.assertTrue(board.feed("C", 0, 3)) self.assertFalse(board.feed("C", 0, 3)) # food source empty self.assertTrue(antC.currentFood == 5) - self.assertTrue(board.getField(0, 3).type == FieldTypeEnum.EMPTY) # check food source depleation + self.assertTrue(board.getField(0, 3).type == FieldType.EMPTY) # check food source depleation def test_getAntIdsOfPlayer(self): - board = gameBoard(5, 5) + board = GameBoard(5, 5) board.createBase(1, 1, "player1") board.createAnt(0, 1, "a1", "player1") board.createAnt(0, 0, "a2", "player1") diff --git a/main_package/test_gameServer.py b/tests/test_gameServer.py similarity index 93% rename from main_package/test_gameServer.py rename to tests/test_gameServer.py index 38b9fc2..925c9d3 100644 --- a/main_package/test_gameServer.py +++ b/tests/test_gameServer.py @@ -1,13 +1,7 @@ import json from unittest import TestCase, mock -from main_package.actions import Actions -from main_package.gameBoard import gameBoard -from main_package.fieldEntities.ant import Ant -from main_package.field import FieldTypeEnum -from main_package.fieldEntities.food import Food -from main_package.gameServer import Server - +from hive import Actions, Ant, Food, Field, FieldType, GameBoard, Server class TestServer(TestCase):