From 32d2b9a22a1d548719160ec67071d0dce54e2c72 Mon Sep 17 00:00:00 2001 From: "Alejandro U. Alvarez" Date: Fri, 28 Feb 2020 15:16:33 +0000 Subject: [PATCH 1/4] initial code for the map generation --- .gitignore | 2 +- .vscode/settings.json | 3 ++ Pipfile | 13 ++++++ Pipfile.lock | 36 ++++++++++++++++ main_package/field.py | 17 ++++++-- main_package/gameBoard.py | 19 +++++---- main_package/mapGenerator.py | 79 ++++++++++++++++++++++++++++++++++++ 7 files changed, 156 insertions(+), 13 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 main_package/mapGenerator.py diff --git a/.gitignore b/.gitignore index b6e4761..9fe17bc 100644 --- a/.gitignore +++ b/.gitignore @@ -126,4 +126,4 @@ venv.bak/ dmypy.json # Pyre type checker -.pyre/ +.pyre/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a3e76b4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "${workspaceFolder}/.venv/bin/python" +} \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..98eea7f --- /dev/null +++ b/Pipfile @@ -0,0 +1,13 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +opensimplex = "*" +colorama = "*" + +[requires] +python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..78407d0 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,36 @@ +{ + "_meta": { + "hash": { + "sha256": "67be4a639dccbe9443215ca1195b78b9f55c4716cd032650d9650a48ca9a1824" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "colorama": { + "hashes": [ + "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", + "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" + ], + "index": "pypi", + "version": "==0.4.3" + }, + "opensimplex": { + "hashes": [ + "sha256:53f12be10faecd6158b406b7af3a66f992435c3b86fca6041f5a14c7979e172b" + ], + "index": "pypi", + "version": "==0.2" + } + }, + "develop": {} +} diff --git a/main_package/field.py b/main_package/field.py index 7fa9c4e..b40ae8c 100644 --- a/main_package/field.py +++ b/main_package/field.py @@ -11,25 +11,34 @@ logging.basicConfig(level=logging.INFO) class FieldTypeEnum(Enum): - EMPTY = Fore.LIGHTBLACK_EX + "E" BASE = Fore.RED + "B" ANT = Fore.BLUE + "A" FOOD = Fore.GREEN + "F" + GRASS = Fore.LIGHTBLACK_EX + "g" + FOREST = Fore.LIGHTBLACK_EX + "F" + WATER = Fore.LIGHTBLACK_EX + "w" + DEEP_WATER = Fore.LIGHTBLACK_EX + "W" + ROCK = Fore.LIGHTBLACK_EX + "R" + SAND = Fore.LIGHTBLACK_EX + "S" + DRY_GRASS = Fore.LIGHTBLACK_EX + "D" + TALL_GRASS = Fore.LIGHTBLACK_EX + "G" + class Field: log = logging.getLogger(__name__) - def __init__(self, xpos:int, ypos:int): + def __init__(self, xpos:int, ypos:int, type: FieldTypeEnum): self.xpos = xpos self.ypos = ypos - self.type = FieldTypeEnum.EMPTY + self.emptyType = type + self.type = type self.entity = None def getPos(self) -> Tuple[int, int]: return self.xpos, self.ypos def resetToEmpty(self): - self.type = FieldTypeEnum.EMPTY + self.type = self.emptyType self.entity = None def setEntity(self, entity) -> bool: diff --git a/main_package/gameBoard.py b/main_package/gameBoard.py index 4379581..d83bd83 100644 --- a/main_package/gameBoard.py +++ b/main_package/gameBoard.py @@ -1,6 +1,7 @@ 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 @@ -12,7 +13,7 @@ class gameBoard: log = logging.getLogger(__name__) validForAttack = [FieldTypeEnum.ANT, FieldTypeEnum.BASE] - def __init__(self, xdim: int = 10, ydim: int = 10): + def __init__(self, xdim: int = 50, ydim: int = 40): """ Initializes an empty board of the given dimensions :param xdim: x dimension (exclusive) @@ -20,7 +21,9 @@ def __init__(self, xdim: int = 10, ydim: int = 10): """ self.xdim = xdim self.ydim = ydim - self.gameBoard = [[Field(xpos=x, ypos=y) for x in range(xdim)] for y in range(ydim)] + + mapGen = MapGenerator(xdim, ydim) + self.gameBoard = mapGen.map self.ants: dict[str, Ant] = {} self.playerBases = {} self.players = [] @@ -49,8 +52,8 @@ 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.EMPTY: - logging.error("Base cannot be placed on field that is not empty. Field is {}".format(field.type)) + if field.type != FieldTypeEnum.GRASS: + logging.error("Base cannot be placed on field that is not grass. Field is {}".format(field.type)) return False if player in self.playerBases.keys(): @@ -130,8 +133,8 @@ def createAnt(self, xpos: int, ypos: int, antId: str, player: str) -> bool: if not any(f.type == FieldTypeEnum.BASE for f in neighbouring_fields): self.log.error("Invalid Placement, no adjacent base") return False - elif placementDesitnation.type is not FieldTypeEnum.EMPTY: - self.log.error("Invalid Placement, field not empty") + elif placementDesitnation.type is not FieldTypeEnum.GRASS: + self.log.error("Invalid Placement, field not grass") return False # check if player owns base near which they want to place ant @@ -156,7 +159,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.EMPTY, fields) + validFields = filter(lambda x: x.type != FieldTypeEnum.ROCK, fields) # is movement valid ? fieldToMoveTo: Field = None @@ -231,7 +234,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.EMPTY: + if targetField is None or targetField.type is not FieldTypeEnum.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 diff --git a/main_package/mapGenerator.py b/main_package/mapGenerator.py new file mode 100644 index 0000000..09cacfd --- /dev/null +++ b/main_package/mapGenerator.py @@ -0,0 +1,79 @@ +from main_package.field import * +from opensimplex import OpenSimplex + +class MapGenerator: + resolution = 5 + mapScale = 30 # bigger => softer land feature + moistureScale = 70 # bigger => softer land feature + + waterMaxElevation = 20 + sandMaxElevation = 2 + + rockMinElevation = 60 + + grassMinMoisture = 45 + tallGrassMinElevation = 55 + tallGrassMinMoisture = 40 + + forestMinMoisture = 0 + forestMaxMoisture = 0 + forestMaxElevation = 0 + + def __init__(self, width: int, height: int): + """ + Initializes a map board of the given dimensions + :param xdim: width (exclusive) + :param ydim: height (exclusive) + """ + self.width = width + self.height = height + self.noiseGenerator = OpenSimplex() + self.map = self.createMap() + + def getTerrain(self, elevation: int, moisture: int) -> FieldTypeEnum: + e = elevation * 100 # elevation [0, 100] + m = moisture * 100 # moisture [0, 100] + + if (e < self.waterMaxElevation / 3): + return FieldTypeEnum.DEEP_WATER + if (e < self.waterMaxElevation): + return FieldTypeEnum.WATER + if (e < self.waterMaxElevation + self.sandMaxElevation): + return FieldTypeEnum.SAND + + if (e > self.rockMinElevation): + return FieldTypeEnum.ROCK + if (e > self.rockMinElevation - self.sandMaxElevation): + return FieldTypeEnum.TALL_GRASS + + if (m < self.grassMinMoisture): + return FieldTypeEnum.DRY_GRASS + if (e < self.forestMaxElevation and m > self.forestMinMoisture and m < self.forestMaxMoisture): + return FieldTypeEnum.FOREST + if (e > self.tallGrassMinElevation and m > self.tallGrassMinMoisture): + return FieldTypeEnum.TALL_GRASS + + return FieldTypeEnum.GRASS + + def createMap(self): + map = [] + + for x in range(self.width / self.resolution): + map[x] = [] + + for y in range(self.height / self.resolution): + elevationValue = self.getNoise(x, y, self.mapScale) + moistureValue = self.getNoise(x, y, self.moistureScale) + + # Now use the noise values to determine the block type + terrainType = self.getTerrain(elevationValue, moistureValue) + + map[x][y] = Field(xpos=x, ypos=y, type=terrainType) + + return map + + def getNoise(self, x: int, y: int, noiseScale: int): + return self.noiseGenerator.noise2D( + x = x / noiseScale, + y = y / noiseScale + ) / 2 + 0.5 \ No newline at end of file From 8a47d93a314c393b4a8a16437e71811028eec434 Mon Sep 17 00:00:00 2001 From: Bharat Reddy Date: Fri, 28 Feb 2020 15:40:41 +0000 Subject: [PATCH 2/4] Create test workflow --- .github/workflows/test.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..73e1394 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,30 @@ +name: Python application + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Lint with flake8 + run: | + pip install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pip install pytest + pytest From 70dd66dfb4c679d51fac8f5ee9268bee6bbbef0d Mon Sep 17 00:00:00 2001 From: Bharat Reddy Date: Fri, 28 Feb 2020 16:03:46 +0000 Subject: [PATCH 3/4] Use pipenv --- .github/workflows/test.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 73e1394..e92d4a3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,18 +13,18 @@ jobs: uses: actions/setup-python@v1 with: python-version: 3.8 + - name: Install pipenv + uses: dschep/install-pipenv-action@v1 - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - name: Lint with flake8 - run: | - pip install flake8 - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + pipenv install --dev + #- name: Lint with flake8 + # run: | + # pip install flake8 + # # stop the build if there are Python syntax errors or undefined names + # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with pytest run: | - pip install pytest - pytest + pipenv run pytest From 688612336d744281df44a74d56eb35cbe28f052a Mon Sep 17 00:00:00 2001 From: Bharat Reddy Date: Fri, 28 Feb 2020 16:32:10 +0000 Subject: [PATCH 4/4] Add readme and pytest dependency --- Pipfile | 1 + Pipfile.lock | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++-- README.md | 7 ++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 README.md diff --git a/Pipfile b/Pipfile index 98eea7f..dd56b2a 100644 --- a/Pipfile +++ b/Pipfile @@ -4,6 +4,7 @@ url = "https://pypi.org/simple" verify_ssl = true [dev-packages] +pytest = "*" [packages] opensimplex = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 78407d0..ca07f09 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "67be4a639dccbe9443215ca1195b78b9f55c4716cd032650d9650a48ca9a1824" + "sha256": "2c3d7250907269b75745b5c47faafa6dfb42d0807d2964e8ee1da5bddf20c665" }, "pipfile-spec": 6, "requires": { @@ -32,5 +32,70 @@ "version": "==0.2" } }, - "develop": {} + "develop": { + "attrs": { + "hashes": [ + "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", + "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" + ], + "version": "==19.3.0" + }, + "more-itertools": { + "hashes": [ + "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c", + "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507" + ], + "version": "==8.2.0" + }, + "packaging": { + "hashes": [ + "sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73", + "sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334" + ], + "version": "==20.1" + }, + "pluggy": { + "hashes": [ + "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", + "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" + ], + "version": "==0.13.1" + }, + "py": { + "hashes": [ + "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa", + "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0" + ], + "version": "==1.8.1" + }, + "pyparsing": { + "hashes": [ + "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", + "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" + ], + "version": "==2.4.6" + }, + "pytest": { + "hashes": [ + "sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d", + "sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6" + ], + "index": "pypi", + "version": "==5.3.5" + }, + "six": { + "hashes": [ + "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", + "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" + ], + "version": "==1.14.0" + }, + "wcwidth": { + "hashes": [ + "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603", + "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8" + ], + "version": "==0.1.8" + } + } } diff --git a/README.md b/README.md new file mode 100644 index 0000000..7627be9 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Running Locally + +``` +PIPENV_VENV_IN_PROJECT=1 +pipenv install +``` +